diff --git a/vrchat_dart/example/bin/main.dart b/vrchat_dart/example/bin/main.dart index a01aaa49..ecccd08b 100644 --- a/vrchat_dart/example/bin/main.dart +++ b/vrchat_dart/example/bin/main.dart @@ -19,23 +19,24 @@ void main() async { ), ); - final loginResponse = await api.auth.login( + final (loginSuccess, loginFailure) = await api.auth.login( username: Credentials.username, password: Credentials.password, ); - if (loginResponse.failure != null) { - print('authError'); - print(loginResponse.failure); - throw Exception('Login failed'); + if (loginSuccess == null) { + print('Login failed'); + print(loginFailure); + return; } - final authResponse = loginResponse.success!.data; + final authResponse = loginSuccess.data; if (authResponse.requiresTwoFactorAuth) { print('requiresTwoFactorAuth'); if (!authResponse.twoFactorAuthTypes.contains(TwoFactorAuthType.totp)) { - throw Exception('Cannot automatically handle 2FA'); + print('Cannot automatically handle 2FA'); + return; } // VRChat is forcing 2FA these days. If you don't have 2FA enabled on your @@ -48,18 +49,20 @@ void main() async { algorithm: Algorithm.SHA1, isGoogle: true, ); - final twoFactorResponse = await api.auth.verify2fa(code); - if (twoFactorResponse.failure == null) { + final (twoFactorSuccess, twoFactorFailure) = await api.auth.verify2fa(code); + if (twoFactorFailure == null) { print('2fa verification success'); } else { print('2fa verification failure'); - print(twoFactorResponse.failure); + print(twoFactorFailure); + return; } } final currentUser = api.auth.currentUser; if (currentUser == null) { - throw Exception('Login failed'); + print('Login failed'); + return; } print('Logged in as ${currentUser.displayName}'); @@ -68,37 +71,58 @@ void main() async { currentUser.toUser(); currentUser.toLimitedUser(); - final friendsResponse = await api.rawApi + final (friendsSuccess, friendsFailure) = await api.rawApi .getFriendsApi() .getFriends() .validateVrc(); // Call [validateVrc] to handle errors - final error = friendsResponse.failure?.vrcError; - if (error != null) { - print(error); + if (friendsSuccess == null) { + print('Fetching friends failed'); + print(friendsFailure); + return; } - final tupper = - (await api.rawApi.getUsersApi().getUser(userId: tupperUid)).data!; + final (tupperSuccess, tupperFailure) = + await api.rawApi.getUsersApi().getUser(userId: tupperUid).validateVrc(); + + if (tupperSuccess == null) { + print('Fetching tupper failed'); + print(tupperFailure); + return; + } // Convenience method to help with storing user objects from different endpoints together - final limitedTupper = tupper.toLimitedUser(); + final limitedTupper = tupperSuccess.data.toLimitedUser(); final friendsAndTupper = [ limitedTupper, - ...friendsResponse.success!.data.map((e) => e.toLimitedUser()), + ...friendsSuccess.data.map((e) => e.toLimitedUser()), ]; print(friendsAndTupper.first.displayName); - final worldsResponse = await api.rawApi + final (worldsSuccess, worldsFailure) = await api.rawApi .getWorldsApi() - .searchWorlds(releaseStatus: ReleaseStatus.public); - print(worldsResponse.data!.first.name); + .searchWorlds(releaseStatus: ReleaseStatus.public) + .validateVrc(); + if (worldsSuccess == null) { + print('Fetching worlds failed'); + print(worldsFailure); + return; + } - final getWorldResponse = await api.rawApi + print(worldsSuccess.data.first.name); + + final (worldSuccess, worldFailure) = await api.rawApi .getWorldsApi() - .getWorld(worldId: worldsResponse.data!.first.id); - print(getWorldResponse.data!.name); + .getWorld(worldId: worldsSuccess.data.first.id) + .validateVrc(); + if (worldSuccess == null) { + print('Fetching world failed'); + print(worldFailure); + return; + } + + print(worldSuccess.data.name); // Do not start websocket streaming if this code is running in CI if (Platform.environment.containsKey('GITHUB_ACTIONS')) return; diff --git a/vrchat_dart/lib/src/api/src/auth_api.dart b/vrchat_dart/lib/src/api/src/auth_api.dart index c9b631c3..2297a47c 100644 --- a/vrchat_dart/lib/src/api/src/auth_api.dart +++ b/vrchat_dart/lib/src/api/src/auth_api.dart @@ -19,7 +19,7 @@ class AuthApi { /// /// Logging in without a [username]/[password] will use the stored auth /// session if available - Future> login({ + Future> login({ String? username, String? password, }) async { @@ -28,51 +28,49 @@ class AuthApi { final authorization = base64.encode( utf8.encode('$encodedUsername:$encodedPassword'), ); - final response = await _rawApi + final (success, failure) = await _rawApi .getAuthenticationApi() .getCurrentUser(headers: {'Authorization': 'Basic $authorization'}) .validateVrc(); - final failure = response.failure; - if (failure != null) { + if (success != null) { + _currentUser = success.data; + return (ValidResponse(AuthResponse(), success.response), null); + } else if (failure != null) { final response = failure.response; - - if (response == null) return failure.cast(); + if (response == null) return (null, failure); final data = response.data; - if (data is! Map) return failure.cast(); + if (data is! Map) return (null, failure); final twoFactorAuthTypes = (data['requiresTwoFactorAuth'] as List?) ?.cast() .map(TwoFactorAuthType.values.byName) .toList(); - if (twoFactorAuthTypes != null) { - return ValidatedResponse.success( + if (twoFactorAuthTypes == null) return (null, failure); + + return ( + ValidResponse( AuthResponse(twoFactorAuthTypes: twoFactorAuthTypes), response, - ); - } - - return failure.cast(); + ), + null, + ); + } else { + throw StateError('This should never happen'); } - - final success = response.success!; - _currentUser = success.data; - - return ValidatedResponse.success(AuthResponse(), success.response); } /// Verify a 2fa code - Future> verify2fa( + Future> verify2fa( String code, ) async { - final response = await _rawApi + final (success, failure) = await _rawApi .getAuthenticationApi() .verify2FA(twoFactorAuthCode: TwoFactorAuthCode(code: code)) .validateVrc(); - final failure = response.failure; - if (failure != null) return failure.cast(); + if (failure != null) return (null, failure); // Call the login function to set the [currentUser] return login(); @@ -81,7 +79,7 @@ class AuthApi { /// Logout /// /// This will set [currentUser] to null - Future> logout() async { + Future> logout() async { final response = await _rawApi .getAuthenticationApi() .logout() diff --git a/vrchat_dart/lib/src/model/api/vrc_response.dart b/vrchat_dart/lib/src/model/api/vrc_response.dart index b1a570d4..0914a171 100644 --- a/vrchat_dart/lib/src/model/api/vrc_response.dart +++ b/vrchat_dart/lib/src/model/api/vrc_response.dart @@ -78,8 +78,8 @@ class VrcError { /// Extension on [Dio] [Response] futures for validation extension VrcResponseValidator on Future> { /// Validate a VRC response, and transform the error to a [VrcError] - Future> validateVrc() => - validate(transformDioError: (e) => VrcError.fromDioError(e) ?? e); + Future> validateVrc() => + validate().transform(dioException: (e) => VrcError.fromDioError(e) ?? e); } /// Extension on [ValidatedResponse] to get the [VrcError] if it exists diff --git a/vrchat_dart/pubspec.yaml b/vrchat_dart/pubspec.yaml index 6a177e4d..6c21ca4c 100644 --- a/vrchat_dart/pubspec.yaml +++ b/vrchat_dart/pubspec.yaml @@ -13,7 +13,7 @@ dependencies: dio: ^5.2.0 web_socket_channel: ^3.0.0 json_annotation: ^4.7.0 - dio_response_validator: ^0.2.1 + dio_response_validator: ^0.3.1 meta: ^1.16.0 dev_dependencies: