Code Monkey home page Code Monkey logo

Comments (13)

jonataslaw avatar jonataslaw commented on May 13, 2024

I'm currently displaying a dialog when my API throws an error.

The problem I'm running into is that I'm making mulitple calls at the same time, and sometimes I get multiple errors, so I end up with getting multiple dialogs open at the same time.

Is there a way to prevent this? Or is there a way to figure out if there is currently a dialog open? So I can check for that before displaying the dialog.

This code snippet should resolve most of your issues.
You are exposing the routing of your Middleware in a Global way through a singleton.
There are endless ways to do this, but if you are not using any state manager, it is the most practical way to do this.

class MiddleWare {
  static MiddleWare _instance;

  factory MiddleWare() {
    if (_instance == null) _instance = MiddleWare._();
    return _instance;
  }

  MiddleWare._();

  static Routing _routing;

  static Routing get routing => _routing;

  static observer(Routing rt) {
    _routing = rt;
  }
}

class Foo {
  openDialog() {
    if(!MiddleWare.routing.isDialog){
   // or if(MiddleWare.routing.isDialog) Get.back(); to close dialog and open other
      Get.dialog(YourDialogClass())
    }
  }
}

Middleware.rounting will give you everything you need. Current route, if you have an open dialog, if you have an open snackbar, if you have an open bottomsheet, if the open route is "x" or "y", and anyway, you can use this approach.
Later on you can create your own approach using your favorite state manager, or reactive programming.

from getx.

jlubeck avatar jlubeck commented on May 13, 2024

Looks like whatever sets the routing.isDialog to true is not fast enough, because I'm still getting dual dialogs when 2 api connections fail at the same time.

Any other ideas?

from getx.

jonataslaw avatar jonataslaw commented on May 13, 2024

if(!MiddleWare.routing.isDialog){ is on 2 apis ?

from getx.

jonataslaw avatar jonataslaw commented on May 13, 2024

I'm trying to reproduce the problem, but I'm not getting it.
Are you making two requests to the api at the same time, and are you turning off the internet or something? or is the error an error response from your own API?
This should not happen for the simple reason that there are no parallel routes. As soon as the dialog is opened, it instantly notifies Middleware. Unless the second dialog is called before the first is opened.
It is a case to think about.

from getx.

jonataslaw avatar jonataslaw commented on May 13, 2024

For testing purposes only, does adding await before Get.dialog change anything?
await Get.dialog(YourDialogClass());

from getx.

jlubeck avatar jlubeck commented on May 13, 2024

It's multiple requests to an api that expires an auth token.

This is my request method:

  static Future<dynamic> post(String endpoint, {Object params}) async {
    Map<String, String> headers = {"Content-Type": "application/json"};

    LoginResultsDTO loginResults = CurrentUser().loginResults;
    if (loginResults?.oAuth?.accessToken != null &&
        loginResults?.oAuth?.tokenType != null) {
      headers['Authorization'] =
          loginResults.oAuth.tokenType + ' ' + loginResults.oAuth.accessToken;
    }

    final body = params == null ? null : jsonEncode(params);
    print('\n${URLS.BASE_URL}$endpoint\n${params ?? ''}');
    final response = await http.post('${URLS.BASE_URL}$endpoint',
        headers: headers, body: body);
    if (response.statusCode == 200) {
      if (response.body.length == 0) return true;
      return json.decode(response.body);
    } else {
      if (response.body != null) {
        ErrorDTO error = ErrorDTO.fromJson(json.decode(response.body));
        if (error.raw != null) {
          switch (error.code) {
            case ServerError.authorizationExpired:
              print('Try to refresh token');
              if (loginResults != null) {
                OAuthDTO refreshOAuth = await AuthenticationService.refresh(
                    loginResults.oAuth?.refreshToken,
                    loginResults.user?.userID);

                var refreshLoginResults =
                    LoginResultsDTO.withOAuth(refreshOAuth, loginResults);
                CurrentUser().setLoginResults(refreshLoginResults);
                return await Api.post(endpoint, params: params);
              }
              throw ServerException('No login results', error);
              break;
            case ServerError.authorizationFailed:
            case ServerError.invalidAuthorizationToken:
              CurrentUser().setLoginResults(null);
              if (!MiddleWare.routing.isDialog) {
                print('error dialog');
                await Get.defaultDialog(
                  title: 'Error',
                  content: Text(error.code.description),
                  confirm: FlatButton(
                    child: Text('OK'),
                    onPressed: () {
                      Get.until(Routes.Login, (Route route) {
                        return route.isFirst;
                      });
                    },
                  ),
                );
              }
              //throw ServerException(error.code.description, error);
              break;
            case ServerError.Unknown:
              print(error.raw);
              throw ServerException(error.raw, error);
              break;
          }
        }
      } else {
        print(response.toString());
        throw Exception('Failed');
      }
    }
  }

from getx.

jonataslaw avatar jonataslaw commented on May 13, 2024

Well, I did a lot of tests, including sending the context by parameter to test if this behavior is a Get problem, and I found out that this is the default behavior of the framework, and that you must implement your own logic to prevent this from happening.

I didn't close this question because I intend to help you with that.

Are you sure your error code is not making a double call? Try this

 static Future<dynamic> post(String endpoint, {Object params}) async {
    Map<String, String> headers = {"Content-Type": "application/json"};

    bool isDialogOpen = false; // ADD THIS

    LoginResultsDTO loginResults = CurrentUser().loginResults;
    if (loginResults?.oAuth?.accessToken != null &&
        loginResults?.oAuth?.tokenType != null) {
      headers['Authorization'] =
          loginResults.oAuth.tokenType + ' ' + loginResults.oAuth.accessToken;
    }

    final body = params == null ? null : jsonEncode(params);
    print('\n${URLS.BASE_URL}$endpoint\n${params ?? ''}');
    final response = await http.post('${URLS.BASE_URL}$endpoint',
        headers: headers, body: body);
    if (response.statusCode == 200) {
      if (response.body.length == 0) return true;
      return json.decode(response.body);
    } else {
      if (response.body != null) {
        ErrorDTO error = ErrorDTO.fromJson(json.decode(response.body));
        if (error.raw != null) {
          switch (error.code) {
            case ServerError.authorizationExpired:
              print('Try to refresh token');
              if (loginResults != null) {
                OAuthDTO refreshOAuth = await AuthenticationService.refresh(
                    loginResults.oAuth?.refreshToken,
                    loginResults.user?.userID);

                var refreshLoginResults =
                    LoginResultsDTO.withOAuth(refreshOAuth, loginResults);
                CurrentUser().setLoginResults(refreshLoginResults);
                return await Api.post(endpoint, params: params);
              }
              throw ServerException('No login results', error);
              break;
            case ServerError.authorizationFailed:
            case ServerError.invalidAuthorizationToken:
              CurrentUser().setLoginResults(null);
              if (!isDialogOpen) { // CHANGE THIS
               isDialogOpen = true;
                print('error dialog');
                 Get.defaultDialog(
                  title: 'Error',
                  content: Text(error.code.description),
                  confirm: FlatButton(
                    child: Text('OK'),
                    onPressed: () {
                      Get.until(Routes.Login, (Route route) {
                        return route.isFirst;
                      });
                    },
                  ),
                );
              }
              //throw ServerException(error.code.description, error);
              break;
            case ServerError.Unknown:
              print(error.raw);
              throw ServerException(error.raw, error);
              break;
          }
        }
      } else {
        print(response.toString());
        throw Exception('Failed');
      }
    }
  }

from getx.

jlubeck avatar jlubeck commented on May 13, 2024

Ok, here is a fully reproducible code:

import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:http/http.dart' as http;
import 'package:rpm/middleware.dart';

class TestScreen extends StatelessWidget {
  void login() {
    getTestError();
    getTestError();
  }

  Future<dynamic> getTestError() async {
    final response =
        await http.get('http://www.mocky.io/v2/5e97d4673000007900b6e08c');
    if (response.statusCode == 200) {
      return response.body;
    } else {
      if (MiddleWare.routing.isDialog == null ||
          MiddleWare.routing.isDialog == false) {
        print('error dialog');
        await Get.defaultDialog(
          title: 'Error',
          content: Text('There was an error'),
          confirm: FlatButton(
            child: Text('OK'),
            onPressed: () {
              Get.back();
            },
          ),
        );
      }
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Container(
          child: FlatButton(
            child: Text('Test'),
            onPressed: login,
          ),
        ),
      ),
    );
  }
}

Your addition with the isDialogOpen doesn't help either

from getx.

jonataslaw avatar jonataslaw commented on May 13, 2024

Thanks for opening this issue, after the reproduction code I was able to trace the root of the problem, and GetObserver is improved now. Can you reproduce the problem using version 1.16.1-dev?
Unfortunately my Macbook is not turning on (I burn the midnight oil trying to get it to turn on), and I made the correction to the stable branch that was installed on my personal notebook. I made the correction to the dev branch by the text editor, so ... I'm not sure it's working, but I hope it works for you.

from getx.

jlubeck avatar jlubeck commented on May 13, 2024

Nice!! It seems to be working on the test snippet. I'll implement it in the app now and let you know how it goes

from getx.

jlubeck avatar jlubeck commented on May 13, 2024

Flawless! Thanks!!

from getx.

jonataslaw avatar jonataslaw commented on May 13, 2024

New API added

import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:http/http.dart' as http;
import 'package:rpm/middleware.dart';

class TestScreen extends StatelessWidget {
  void login() {
    getTestError();
    getTestError();
  }

  Future<dynamic> getTestError() async {
    final response =
        await http.get('http://www.mocky.io/v2/5e97d4673000007900b6e08c');
    if (response.statusCode == 200) {
      return response.body;
    } else {
      if (!Get.isDialogOpen) { // Now you can made this
        print('error dialog');
        await Get.defaultDialog(
          title: 'Error',
          content: Text('There was an error'),
          confirm: FlatButton(
            child: Text('OK'),
            onPressed: () {
              Get.back();
            },
          ),
        );
      }
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Container(
          child: FlatButton(
            child: Text('Test'),
            onPressed: login,
          ),
        ),
      ),
    );
  }
}

from getx.

jlubeck avatar jlubeck commented on May 13, 2024

Great! I verified it. Works like a charm

from getx.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.