Skip to content

Commit

Permalink
Fix issue updating user data over websocket (#1055)
Browse files Browse the repository at this point in the history
* send user data over websocket channel

* revert changes

* Add userdata polling when user generate payments links.

* remove logs.

* Added custom exception and added 2 second delay factor.

---------

Co-authored-by: Jigar-f <[email protected]>
  • Loading branch information
atavism and jigar-f authored Apr 26, 2024
1 parent ca793a5 commit f120116
Show file tree
Hide file tree
Showing 8 changed files with 100 additions and 14 deletions.
2 changes: 1 addition & 1 deletion desktop/app/issue.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ func (reporter *issueReporter) sendIssueReport(msg *issueMessage) error {
}
subscriptionLevel := "free"
ctx := context.Background()
if isPro, _ := IsProUser(ctx, reporter.proClient, settings.GetUserID()); isPro {
if isPro, _ := IsProUser(ctx, reporter.proClient, settings); isPro {
subscriptionLevel = "pro"
}
var osVersion string
Expand Down
26 changes: 22 additions & 4 deletions desktop/app/pro.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,17 +86,17 @@ func (app *App) IsProUser(ctx context.Context) (isPro bool, ok bool) {
if err != nil {
return false, false
}
return IsProUser(ctx, app.proClient, app.settings.GetUserID())
return IsProUser(ctx, app.proClient, app.settings)
}

func IsProUser(ctx context.Context, proClient pro.ProClient, userId int64) (isPro bool, ok bool) {
func IsProUser(ctx context.Context, proClient pro.ProClient, uc common.UserConfig) (isPro bool, ok bool) {
isActive := func(user *protos.User) bool {
return user != nil && user.UserStatus == "active"
}
user, found := GetUserDataFast(ctx, userId)
user, found := GetUserDataFast(ctx, uc.GetUserID())
if !found {
ctx := context.Background()
resp, err := proClient.UserData(ctx)
resp, err := fetchUserDataWithClient(ctx, proClient, uc)
if err != nil {
return false, false
}
Expand All @@ -105,6 +105,24 @@ func IsProUser(ctx context.Context, proClient pro.ProClient, userId int64) (isPr
return isActive(user), true
}

func fetchUserDataWithClient(ctx context.Context, proClient pro.ProClient, uc common.UserConfig) (*pro.UserDataResponse, error) {
userID := uc.GetUserID()
log.Debugf("Fetching user status with device ID '%v', user ID '%v' and proToken %v",
uc.GetDeviceID(), userID, uc.GetToken())
resp, err := proClient.UserData(ctx)
if err != nil {
return nil, err
}
setUserData(ctx, userID, resp.User)
log.Debugf("User %d is '%v'", userID, resp.User.UserStatus)
return resp, nil
}

func setUserData(ctx context.Context, userID int64, user *protos.User) {
log.Debugf("Storing user data for user %v", userID)
userData.save(ctx, userID, user)
}

// isActive determines whether the given status is an active status
func isActive(status string) bool {
return status == "active"
Expand Down
26 changes: 23 additions & 3 deletions desktop/lib.go
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,6 @@ func plans() *C.char {
}
paymentMethodsResponse := &proclient.PaymentMethodsResponse{}
err := json.Unmarshal(plans, paymentMethodsResponse)
log.Debugf("DEBUG: cache payment methods found : %v", paymentMethodsResponse.Plans)
plansByte, err := json.Marshal(paymentMethodsResponse.Plans)
if err != nil {
return sendError(errors.New("error fetching payment methods: %v", err))
Expand Down Expand Up @@ -238,7 +237,6 @@ func paymentMethodsV4() *C.char {
if err != nil {
return sendError(err)
}
log.Debugf("DEBUG: cache payment methods: %v", paymentMethodsResponse.Providers)
b, _ := json.Marshal(paymentMethodsResponse)
return C.CString(string(b))
}
Expand All @@ -255,6 +253,28 @@ func getUserData() (*protos.User, error) {
return user, nil
}

// this method is reposible for checking if the user has updated plan or bought plans
//
//export hasPlanUpdatedOrBuy
func hasPlanUpdatedOrBuy() *C.char {
//Get the cached user data
log.Debugf("DEBUG: Checking if user has updated plan or bought new plan")
uc := userConfig(a.Settings())
cahcheUserData, isOldFound := app.GetUserDataFast(context.Background(), uc.GetUserID())
//Get latest user data
resp, err := proClient.UserData(context.Background())
if err != nil {
return sendError(err)
}
if isOldFound {
if cahcheUserData.Expiration < resp.User.Expiration {
// New data has a later expiration
return C.CString(string("true"))
}
}
return C.CString(string("false"))
}

//export devices
func devices() *C.char {
user, err := getUserData()
Expand Down Expand Up @@ -459,7 +479,7 @@ func acceptedTermsVersion() *C.char {
func proUser() *C.char {
ctx := context.Background()
// refresh user data when home page is loaded on desktop
go proClient.UserData(ctx)
go getUserData()
uc := a.Settings()
if isProUser, ok := app.IsProUserFast(ctx, uc); isProUser && ok {
return C.CString("true")
Expand Down
2 changes: 1 addition & 1 deletion lib/account/account.dart
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ class AccountMenu extends StatelessWidget {
borderRadius: BorderRadius.only(
topLeft: Radius.circular(16), topRight: Radius.circular(16))),
builder: (context) {
return FollowUs();
return const FollowUs();
},
);
}
Expand Down
5 changes: 5 additions & 0 deletions lib/common/session_model.dart
Original file line number Diff line number Diff line change
Expand Up @@ -479,6 +479,11 @@ class SessionModel extends Model {
return methodChannel.invokeMethod('updatePaymentPlans');
}

Future<bool> hasUpdatePlansOrBuy() async {
return compute(ffiHasPlanUpdateOrBuy,'');
}


Plan planFromJson(Map<String, dynamic> item) {
print("called plans $item");
final locale = Localization.locale;
Expand Down
25 changes: 21 additions & 4 deletions lib/ffi.dart
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,10 @@ Future<User> ffiUserData() async {

// checkAPIError throws a PlatformException if the API response contains an error
void checkAPIError(result, errorMessage) {
if(result is String){
final errorMessageMap = jsonDecode(result);
throw PlatformException(code:errorMessageMap.toString(), message: errorMessage);
if (result is String) {
final errorMessageMap = jsonDecode(result);
throw PlatformException(
code: errorMessageMap.toString(), message: errorMessage);
}
if (result.error != "") {
throw PlatformException(code: result.error, message: errorMessage);
Expand Down Expand Up @@ -80,6 +81,12 @@ Future<void> ffiRemoveDevice(String deviceId) async {
return;
}

FutureOr<bool> ffiHasPlanUpdateOrBuy(dynamic context) {
final json = _bindings.hasPlanUpdatedOrBuy().cast<Utf8>().toDartString();
print('Result of hasPlanUpdatedOrBuy: $json');
return json == 'true' ? true : throw NoPlansUpdate("No Plans update");
}

Pointer<Utf8> ffiDevices() => _bindings.devices().cast<Utf8>();

Pointer<Utf8> ffiDevelopmentMode() => _bindings.developmentMode().cast<Utf8>();
Expand Down Expand Up @@ -117,7 +124,9 @@ Pointer<Utf8> ffiCheckUpdates() => _bindings.checkUpdates().cast<Utf8>();
Pointer<Utf8> ffiPlans() => _bindings.plans().cast<Utf8>();

Pointer<Utf8> ffiPaymentMethods() => _bindings.paymentMethodsV3().cast<Utf8>();
Pointer<Utf8> ffiPaymentMethodsV4() => _bindings.paymentMethodsV4().cast<Utf8>();

Pointer<Utf8> ffiPaymentMethodsV4() =>
_bindings.paymentMethodsV4().cast<Utf8>();

Pointer<Utf8> ffiDeviceLinkingCode() =>
_bindings.deviceLinkingCode().cast<Utf8>();
Expand Down Expand Up @@ -186,3 +195,11 @@ final NativeLibrary _bindings = NativeLibrary(_dylib);
void loadLibrary() {
_bindings.start();
}

//Custom exception for handling error

class NoPlansUpdate implements Exception {
String message;

NoPlansUpdate(this.message);
}
26 changes: 26 additions & 0 deletions lib/plans/checkout.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import 'package:lantern/common/common_desktop.dart';
import 'package:lantern/plans/payment_provider.dart';
import 'package:lantern/plans/plan_details.dart';
import 'package:lantern/plans/utils.dart';
import 'package:retry/retry.dart';

@RoutePage(name: 'Checkout')
class Checkout extends StatefulWidget {
Expand Down Expand Up @@ -385,6 +386,8 @@ class _CheckoutState extends State<Checkout>
provider: provider,
redirectUrl: redirectUrl,
onClose: checkProUser);
//as soon user click we should start polling userData
Future.delayed(const Duration(seconds: 2), hasPlansUpdateOrBuy);
} catch (error, stackTrace) {
context.loaderOverlay.hide();
showError(context, error: error, stackTrace: stackTrace);
Expand Down Expand Up @@ -473,4 +476,27 @@ class _CheckoutState extends State<Checkout>
return false;
}
}

///This methods is responsible for polling for user data
///so if user has done payment or renew plans and show
void hasPlansUpdateOrBuy() {
appLogger.i("calling hasPlansUpdateOrBuy to update plans or buy");
try {
retry(
() async {
/// Polling for userData that user has updates plans or buy
final plansUpdated = await sessionModel.hasUpdatePlansOrBuy();
if (plansUpdated) {
if (mounted) {
showSuccessDialog(context, widget.isPro);
}
}
},
delayFactor: const Duration(seconds: 2),
retryIf: (e) => e is NoPlansUpdate,
);
} catch (e) {
appLogger.e('Error while polling for plans update or buy', error: e);
}
}
}
2 changes: 1 addition & 1 deletion lib/plans/utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ Future<void> openDesktopWebview(
break;
case 'macos':
if (provider == Providers.fropay.name) {
// Open with system browser browser on mac due to not able to by pass humans verification.
// Open with system browser browser on mac due to not able to by pass human verification.
await InAppBrowser.openWithSystemBrowser(url: WebUri(redirectUrl));
} else {
final browser = AppBrowser(onClose: onClose);
Expand Down

0 comments on commit f120116

Please sign in to comment.