Add Supa account and search changes
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (c) 2024-2026 Fabian Freund.
|
||||
*
|
||||
* This file is part of WebLibre
|
||||
* (see https://weblibre.eu).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import 'package:copy_with_extension/copy_with_extension.dart';
|
||||
import 'package:fast_equatable/fast_equatable.dart';
|
||||
import 'package:supabase/supabase.dart';
|
||||
|
||||
part 'account_auth_state.g.dart';
|
||||
|
||||
enum AccountAuthStatus { signedOut, signingIn, signedIn, error }
|
||||
|
||||
@CopyWith()
|
||||
class AccountAuthState with FastEquatable {
|
||||
final AccountAuthStatus status;
|
||||
final String? email;
|
||||
final String? displayName;
|
||||
final String? userId;
|
||||
final String? lastError;
|
||||
final String? syncKey;
|
||||
// The SupabaseClient is intentionally excluded from equality. Two auth
|
||||
// states for the same user but with distinct client instances are still
|
||||
// semantically equal — including identityHashCode here would defeat
|
||||
// Riverpod's caching by treating every reissued state as different.
|
||||
// ignore: missing_field_in_equatable_props
|
||||
final SupabaseClient? client;
|
||||
|
||||
AccountAuthState({
|
||||
this.status = AccountAuthStatus.signedOut,
|
||||
this.email,
|
||||
this.displayName,
|
||||
this.userId,
|
||||
this.lastError,
|
||||
this.syncKey,
|
||||
this.client,
|
||||
});
|
||||
|
||||
bool get isSignedIn => status == AccountAuthStatus.signedIn;
|
||||
bool get isSignedOut => status == AccountAuthStatus.signedOut;
|
||||
bool get isSigningIn => status == AccountAuthStatus.signingIn;
|
||||
bool get hasError => status == AccountAuthStatus.error;
|
||||
bool get hasSyncKey => syncKey != null;
|
||||
|
||||
@override
|
||||
List<Object?> get hashParameters => [
|
||||
status,
|
||||
email,
|
||||
displayName,
|
||||
userId,
|
||||
lastError,
|
||||
syncKey,
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'account_auth_state.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// CopyWithGenerator
|
||||
// **************************************************************************
|
||||
|
||||
abstract class _$AccountAuthStateCWProxy {
|
||||
AccountAuthState status(AccountAuthStatus status);
|
||||
|
||||
AccountAuthState email(String? email);
|
||||
|
||||
AccountAuthState displayName(String? displayName);
|
||||
|
||||
AccountAuthState userId(String? userId);
|
||||
|
||||
AccountAuthState lastError(String? lastError);
|
||||
|
||||
AccountAuthState syncKey(String? syncKey);
|
||||
|
||||
AccountAuthState client(SupabaseClient? client);
|
||||
|
||||
/// Creates a new instance with the provided field values.
|
||||
/// Passing `null` to a nullable field nullifies it, while `null` for a non-nullable field is ignored. To update a single field use `AccountAuthState(...).copyWith.fieldName(value)`.
|
||||
///
|
||||
/// Example:
|
||||
/// ```dart
|
||||
/// AccountAuthState(...).copyWith(id: 12, name: "My name")
|
||||
/// ```
|
||||
AccountAuthState call({
|
||||
AccountAuthStatus status,
|
||||
String? email,
|
||||
String? displayName,
|
||||
String? userId,
|
||||
String? lastError,
|
||||
String? syncKey,
|
||||
SupabaseClient? client,
|
||||
});
|
||||
}
|
||||
|
||||
/// Callable proxy for `copyWith` functionality.
|
||||
/// Use as `instanceOfAccountAuthState.copyWith(...)` or call `instanceOfAccountAuthState.copyWith.fieldName(value)` for a single field.
|
||||
class _$AccountAuthStateCWProxyImpl implements _$AccountAuthStateCWProxy {
|
||||
const _$AccountAuthStateCWProxyImpl(this._value);
|
||||
|
||||
final AccountAuthState _value;
|
||||
|
||||
@override
|
||||
AccountAuthState status(AccountAuthStatus status) => call(status: status);
|
||||
|
||||
@override
|
||||
AccountAuthState email(String? email) => call(email: email);
|
||||
|
||||
@override
|
||||
AccountAuthState displayName(String? displayName) =>
|
||||
call(displayName: displayName);
|
||||
|
||||
@override
|
||||
AccountAuthState userId(String? userId) => call(userId: userId);
|
||||
|
||||
@override
|
||||
AccountAuthState lastError(String? lastError) => call(lastError: lastError);
|
||||
|
||||
@override
|
||||
AccountAuthState syncKey(String? syncKey) => call(syncKey: syncKey);
|
||||
|
||||
@override
|
||||
AccountAuthState client(SupabaseClient? client) => call(client: client);
|
||||
|
||||
@override
|
||||
/// Creates a new instance with the provided field values.
|
||||
/// Passing `null` to a nullable field nullifies it, while `null` for a non-nullable field is ignored. To update a single field use `AccountAuthState(...).copyWith.fieldName(value)`.
|
||||
///
|
||||
/// Example:
|
||||
/// ```dart
|
||||
/// AccountAuthState(...).copyWith(id: 12, name: "My name")
|
||||
/// ```
|
||||
AccountAuthState call({
|
||||
Object? status = const $CopyWithPlaceholder(),
|
||||
Object? email = const $CopyWithPlaceholder(),
|
||||
Object? displayName = const $CopyWithPlaceholder(),
|
||||
Object? userId = const $CopyWithPlaceholder(),
|
||||
Object? lastError = const $CopyWithPlaceholder(),
|
||||
Object? syncKey = const $CopyWithPlaceholder(),
|
||||
Object? client = const $CopyWithPlaceholder(),
|
||||
}) {
|
||||
return AccountAuthState(
|
||||
status: status == const $CopyWithPlaceholder() || status == null
|
||||
? _value.status
|
||||
// ignore: cast_nullable_to_non_nullable
|
||||
: status as AccountAuthStatus,
|
||||
email: email == const $CopyWithPlaceholder()
|
||||
? _value.email
|
||||
// ignore: cast_nullable_to_non_nullable
|
||||
: email as String?,
|
||||
displayName: displayName == const $CopyWithPlaceholder()
|
||||
? _value.displayName
|
||||
// ignore: cast_nullable_to_non_nullable
|
||||
: displayName as String?,
|
||||
userId: userId == const $CopyWithPlaceholder()
|
||||
? _value.userId
|
||||
// ignore: cast_nullable_to_non_nullable
|
||||
: userId as String?,
|
||||
lastError: lastError == const $CopyWithPlaceholder()
|
||||
? _value.lastError
|
||||
// ignore: cast_nullable_to_non_nullable
|
||||
: lastError as String?,
|
||||
syncKey: syncKey == const $CopyWithPlaceholder()
|
||||
? _value.syncKey
|
||||
// ignore: cast_nullable_to_non_nullable
|
||||
: syncKey as String?,
|
||||
client: client == const $CopyWithPlaceholder()
|
||||
? _value.client
|
||||
// ignore: cast_nullable_to_non_nullable
|
||||
: client as SupabaseClient?,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
extension $AccountAuthStateCopyWith on AccountAuthState {
|
||||
/// Returns a callable class used to build a new instance with modified fields.
|
||||
/// Example: `instanceOfAccountAuthState.copyWith(...)` or `instanceOfAccountAuthState.copyWith.fieldName(...)`.
|
||||
// ignore: library_private_types_in_public_api
|
||||
_$AccountAuthStateCWProxy get copyWith => _$AccountAuthStateCWProxyImpl(this);
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (c) 2024-2026 Fabian Freund.
|
||||
*
|
||||
* This file is part of WebLibre
|
||||
* (see https://weblibre.eu).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import 'package:copy_with_extension/copy_with_extension.dart';
|
||||
import 'package:fast_equatable/fast_equatable.dart';
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
import 'package:weblibre/features/account/data/models/persisted_session.dart';
|
||||
|
||||
part 'account_persisted_data.g.dart';
|
||||
|
||||
@CopyWith()
|
||||
@JsonSerializable()
|
||||
class AccountPersistedData with FastEquatable {
|
||||
final PersistedSession? session;
|
||||
final String? userId;
|
||||
final String? email;
|
||||
final String? displayName;
|
||||
final String? pendingCodeVerifier;
|
||||
final String? syncKey;
|
||||
|
||||
AccountPersistedData({
|
||||
this.session,
|
||||
this.userId,
|
||||
this.email,
|
||||
this.displayName,
|
||||
this.pendingCodeVerifier,
|
||||
this.syncKey,
|
||||
});
|
||||
|
||||
factory AccountPersistedData.fromJson(Map<String, dynamic> json) =>
|
||||
_$AccountPersistedDataFromJson(json);
|
||||
|
||||
Map<String, dynamic> toJson() => _$AccountPersistedDataToJson(this);
|
||||
|
||||
@override
|
||||
List<Object?> get hashParameters => [
|
||||
session,
|
||||
userId,
|
||||
email,
|
||||
displayName,
|
||||
pendingCodeVerifier,
|
||||
syncKey,
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'account_persisted_data.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// CopyWithGenerator
|
||||
// **************************************************************************
|
||||
|
||||
abstract class _$AccountPersistedDataCWProxy {
|
||||
AccountPersistedData session(PersistedSession? session);
|
||||
|
||||
AccountPersistedData userId(String? userId);
|
||||
|
||||
AccountPersistedData email(String? email);
|
||||
|
||||
AccountPersistedData displayName(String? displayName);
|
||||
|
||||
AccountPersistedData pendingCodeVerifier(String? pendingCodeVerifier);
|
||||
|
||||
AccountPersistedData syncKey(String? syncKey);
|
||||
|
||||
/// Creates a new instance with the provided field values.
|
||||
/// Passing `null` to a nullable field nullifies it, while `null` for a non-nullable field is ignored. To update a single field use `AccountPersistedData(...).copyWith.fieldName(value)`.
|
||||
///
|
||||
/// Example:
|
||||
/// ```dart
|
||||
/// AccountPersistedData(...).copyWith(id: 12, name: "My name")
|
||||
/// ```
|
||||
AccountPersistedData call({
|
||||
PersistedSession? session,
|
||||
String? userId,
|
||||
String? email,
|
||||
String? displayName,
|
||||
String? pendingCodeVerifier,
|
||||
String? syncKey,
|
||||
});
|
||||
}
|
||||
|
||||
/// Callable proxy for `copyWith` functionality.
|
||||
/// Use as `instanceOfAccountPersistedData.copyWith(...)` or call `instanceOfAccountPersistedData.copyWith.fieldName(value)` for a single field.
|
||||
class _$AccountPersistedDataCWProxyImpl
|
||||
implements _$AccountPersistedDataCWProxy {
|
||||
const _$AccountPersistedDataCWProxyImpl(this._value);
|
||||
|
||||
final AccountPersistedData _value;
|
||||
|
||||
@override
|
||||
AccountPersistedData session(PersistedSession? session) =>
|
||||
call(session: session);
|
||||
|
||||
@override
|
||||
AccountPersistedData userId(String? userId) => call(userId: userId);
|
||||
|
||||
@override
|
||||
AccountPersistedData email(String? email) => call(email: email);
|
||||
|
||||
@override
|
||||
AccountPersistedData displayName(String? displayName) =>
|
||||
call(displayName: displayName);
|
||||
|
||||
@override
|
||||
AccountPersistedData pendingCodeVerifier(String? pendingCodeVerifier) =>
|
||||
call(pendingCodeVerifier: pendingCodeVerifier);
|
||||
|
||||
@override
|
||||
AccountPersistedData syncKey(String? syncKey) => call(syncKey: syncKey);
|
||||
|
||||
@override
|
||||
/// Creates a new instance with the provided field values.
|
||||
/// Passing `null` to a nullable field nullifies it, while `null` for a non-nullable field is ignored. To update a single field use `AccountPersistedData(...).copyWith.fieldName(value)`.
|
||||
///
|
||||
/// Example:
|
||||
/// ```dart
|
||||
/// AccountPersistedData(...).copyWith(id: 12, name: "My name")
|
||||
/// ```
|
||||
AccountPersistedData call({
|
||||
Object? session = const $CopyWithPlaceholder(),
|
||||
Object? userId = const $CopyWithPlaceholder(),
|
||||
Object? email = const $CopyWithPlaceholder(),
|
||||
Object? displayName = const $CopyWithPlaceholder(),
|
||||
Object? pendingCodeVerifier = const $CopyWithPlaceholder(),
|
||||
Object? syncKey = const $CopyWithPlaceholder(),
|
||||
}) {
|
||||
return AccountPersistedData(
|
||||
session: session == const $CopyWithPlaceholder()
|
||||
? _value.session
|
||||
// ignore: cast_nullable_to_non_nullable
|
||||
: session as PersistedSession?,
|
||||
userId: userId == const $CopyWithPlaceholder()
|
||||
? _value.userId
|
||||
// ignore: cast_nullable_to_non_nullable
|
||||
: userId as String?,
|
||||
email: email == const $CopyWithPlaceholder()
|
||||
? _value.email
|
||||
// ignore: cast_nullable_to_non_nullable
|
||||
: email as String?,
|
||||
displayName: displayName == const $CopyWithPlaceholder()
|
||||
? _value.displayName
|
||||
// ignore: cast_nullable_to_non_nullable
|
||||
: displayName as String?,
|
||||
pendingCodeVerifier: pendingCodeVerifier == const $CopyWithPlaceholder()
|
||||
? _value.pendingCodeVerifier
|
||||
// ignore: cast_nullable_to_non_nullable
|
||||
: pendingCodeVerifier as String?,
|
||||
syncKey: syncKey == const $CopyWithPlaceholder()
|
||||
? _value.syncKey
|
||||
// ignore: cast_nullable_to_non_nullable
|
||||
: syncKey as String?,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
extension $AccountPersistedDataCopyWith on AccountPersistedData {
|
||||
/// Returns a callable class used to build a new instance with modified fields.
|
||||
/// Example: `instanceOfAccountPersistedData.copyWith(...)` or `instanceOfAccountPersistedData.copyWith.fieldName(...)`.
|
||||
// ignore: library_private_types_in_public_api
|
||||
_$AccountPersistedDataCWProxy get copyWith =>
|
||||
_$AccountPersistedDataCWProxyImpl(this);
|
||||
}
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
AccountPersistedData _$AccountPersistedDataFromJson(
|
||||
Map<String, dynamic> json,
|
||||
) => AccountPersistedData(
|
||||
session: json['session'] == null
|
||||
? null
|
||||
: PersistedSession.fromJson(json['session'] as Map<String, dynamic>),
|
||||
userId: json['userId'] as String?,
|
||||
email: json['email'] as String?,
|
||||
displayName: json['displayName'] as String?,
|
||||
pendingCodeVerifier: json['pendingCodeVerifier'] as String?,
|
||||
syncKey: json['syncKey'] as String?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$AccountPersistedDataToJson(
|
||||
AccountPersistedData instance,
|
||||
) => <String, dynamic>{
|
||||
'session': instance.session?.toJson(),
|
||||
'userId': instance.userId,
|
||||
'email': instance.email,
|
||||
'displayName': instance.displayName,
|
||||
'pendingCodeVerifier': instance.pendingCodeVerifier,
|
||||
'syncKey': instance.syncKey,
|
||||
};
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (c) 2024-2026 Fabian Freund.
|
||||
*
|
||||
* This file is part of WebLibre
|
||||
* (see https://weblibre.eu).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import 'package:fast_equatable/fast_equatable.dart';
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
part 'persisted_session.g.dart';
|
||||
|
||||
@JsonSerializable()
|
||||
class PersistedSession with FastEquatable {
|
||||
@JsonKey(name: 'access_token')
|
||||
final String accessToken;
|
||||
|
||||
@JsonKey(name: 'refresh_token')
|
||||
final String refreshToken;
|
||||
|
||||
@JsonKey(name: 'token_type')
|
||||
final String tokenType;
|
||||
|
||||
@JsonKey(name: 'expires_in')
|
||||
final int expiresIn;
|
||||
|
||||
PersistedSession({
|
||||
required this.accessToken,
|
||||
required this.refreshToken,
|
||||
required this.tokenType,
|
||||
required this.expiresIn,
|
||||
});
|
||||
|
||||
factory PersistedSession.fromJson(Map<String, dynamic> json) =>
|
||||
_$PersistedSessionFromJson(json);
|
||||
|
||||
Map<String, dynamic> toJson() => _$PersistedSessionToJson(this);
|
||||
|
||||
@override
|
||||
List<Object?> get hashParameters => [
|
||||
accessToken,
|
||||
refreshToken,
|
||||
tokenType,
|
||||
expiresIn,
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'persisted_session.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
PersistedSession _$PersistedSessionFromJson(Map<String, dynamic> json) =>
|
||||
PersistedSession(
|
||||
accessToken: json['access_token'] as String,
|
||||
refreshToken: json['refresh_token'] as String,
|
||||
tokenType: json['token_type'] as String,
|
||||
expiresIn: (json['expires_in'] as num).toInt(),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$PersistedSessionToJson(PersistedSession instance) =>
|
||||
<String, dynamic>{
|
||||
'access_token': instance.accessToken,
|
||||
'refresh_token': instance.refreshToken,
|
||||
'token_type': instance.tokenType,
|
||||
'expires_in': instance.expiresIn,
|
||||
};
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (c) 2024-2026 Fabian Freund.
|
||||
*
|
||||
* This file is part of WebLibre
|
||||
* (see https://weblibre.eu).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
import 'package:weblibre/features/user/data/models/engine_settings.dart';
|
||||
import 'package:weblibre/features/user/data/models/general_settings.dart';
|
||||
import 'package:weblibre/features/user/data/models/tor_settings.dart';
|
||||
|
||||
part 'settings_sync_envelope.g.dart';
|
||||
|
||||
@JsonSerializable()
|
||||
class SettingsSyncPayload {
|
||||
final GeneralSettings? general;
|
||||
final EngineSettings? engine;
|
||||
final TorSettings? tor;
|
||||
|
||||
SettingsSyncPayload({this.general, this.engine, this.tor});
|
||||
|
||||
factory SettingsSyncPayload.fromJson(Map<String, dynamic> json) =>
|
||||
_$SettingsSyncPayloadFromJson(json);
|
||||
|
||||
Map<String, dynamic> toJson() => _$SettingsSyncPayloadToJson(this);
|
||||
}
|
||||
|
||||
@JsonSerializable()
|
||||
class SettingsSyncEnvelope {
|
||||
@JsonKey(name: 'schema_version')
|
||||
final int schemaVersion;
|
||||
|
||||
@JsonKey(name: 'exported_at')
|
||||
final String exportedAt;
|
||||
|
||||
final SettingsSyncPayload payload;
|
||||
|
||||
SettingsSyncEnvelope({
|
||||
required this.schemaVersion,
|
||||
required this.exportedAt,
|
||||
required this.payload,
|
||||
});
|
||||
|
||||
factory SettingsSyncEnvelope.fromJson(Map<String, dynamic> json) =>
|
||||
_$SettingsSyncEnvelopeFromJson(json);
|
||||
|
||||
Map<String, dynamic> toJson() => _$SettingsSyncEnvelopeToJson(this);
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'settings_sync_envelope.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
SettingsSyncPayload _$SettingsSyncPayloadFromJson(Map<String, dynamic> json) =>
|
||||
SettingsSyncPayload(
|
||||
general: json['general'] == null
|
||||
? null
|
||||
: GeneralSettings.fromJson(json['general'] as Map<String, dynamic>),
|
||||
engine: json['engine'] == null
|
||||
? null
|
||||
: EngineSettings.fromJson(json['engine'] as Map<String, dynamic>),
|
||||
tor: json['tor'] == null
|
||||
? null
|
||||
: TorSettings.fromJson(json['tor'] as Map<String, dynamic>),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$SettingsSyncPayloadToJson(
|
||||
SettingsSyncPayload instance,
|
||||
) => <String, dynamic>{
|
||||
'general': instance.general?.toJson(),
|
||||
'engine': instance.engine?.toJson(),
|
||||
'tor': instance.tor?.toJson(),
|
||||
};
|
||||
|
||||
SettingsSyncEnvelope _$SettingsSyncEnvelopeFromJson(
|
||||
Map<String, dynamic> json,
|
||||
) => SettingsSyncEnvelope(
|
||||
schemaVersion: (json['schema_version'] as num).toInt(),
|
||||
exportedAt: json['exported_at'] as String,
|
||||
payload: SettingsSyncPayload.fromJson(
|
||||
json['payload'] as Map<String, dynamic>,
|
||||
),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$SettingsSyncEnvelopeToJson(
|
||||
SettingsSyncEnvelope instance,
|
||||
) => <String, dynamic>{
|
||||
'schema_version': instance.schemaVersion,
|
||||
'exported_at': instance.exportedAt,
|
||||
'payload': instance.payload.toJson(),
|
||||
};
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright (c) 2024-2026 Fabian Freund.
|
||||
*
|
||||
* This file is part of WebLibre
|
||||
* (see https://weblibre.eu).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import 'package:fast_equatable/fast_equatable.dart';
|
||||
|
||||
class SubscriptionStatus with FastEquatable {
|
||||
final bool hasActiveSubscription;
|
||||
final DateTime? entitledUntil;
|
||||
final String status;
|
||||
final String? subscriptionStatus;
|
||||
final bool cancelAtPeriodEnd;
|
||||
final DateTime? currentPeriodEnd;
|
||||
final String? planLabel;
|
||||
|
||||
SubscriptionStatus({
|
||||
required this.hasActiveSubscription,
|
||||
this.entitledUntil,
|
||||
required this.status,
|
||||
this.subscriptionStatus,
|
||||
required this.cancelAtPeriodEnd,
|
||||
this.currentPeriodEnd,
|
||||
this.planLabel,
|
||||
});
|
||||
|
||||
factory SubscriptionStatus.fromJson(Map<String, dynamic> json) {
|
||||
return SubscriptionStatus(
|
||||
hasActiveSubscription: json['has_active_subscription'] as bool? ?? false,
|
||||
entitledUntil: json['entitled_until'] != null
|
||||
? DateTime.parse(json['entitled_until'] as String)
|
||||
: null,
|
||||
status: json['status'] as String? ?? 'inactive',
|
||||
subscriptionStatus: json['subscription_status'] as String?,
|
||||
cancelAtPeriodEnd: json['cancel_at_period_end'] as bool? ?? false,
|
||||
currentPeriodEnd: json['current_period_end'] != null
|
||||
? DateTime.parse(json['current_period_end'] as String)
|
||||
: null,
|
||||
planLabel: json['plan_label'] as String?,
|
||||
);
|
||||
}
|
||||
|
||||
bool get isActive => hasActiveSubscription;
|
||||
bool get isPaused => subscriptionStatus == 'paused';
|
||||
bool get isPastDue => subscriptionStatus == 'past_due';
|
||||
|
||||
/// Subscription is on its way out: either explicitly scheduled to end at
|
||||
/// period end, already in `canceled` (grace period), or
|
||||
/// `scheduled_cancel`. The user can still resume from the portal until
|
||||
/// the period ends.
|
||||
bool get isWindingDown =>
|
||||
cancelAtPeriodEnd ||
|
||||
subscriptionStatus == 'canceled' ||
|
||||
subscriptionStatus == 'scheduled_cancel';
|
||||
|
||||
/// Whether the backend reports any underlying subscription state at all.
|
||||
/// When false the user has never had a sub (or it was wiped) and the UI
|
||||
/// should offer "Subscribe" rather than a state-specific message.
|
||||
bool get hasSubscriptionRecord => subscriptionStatus != null;
|
||||
|
||||
// Not `const` because `FastEquatable` carries a cached-hash field that
|
||||
// disallows const construction. Value-equality from the mixin means
|
||||
// `SubscriptionStatus.inactive == SubscriptionStatus(...same fields...)`
|
||||
// still holds, which is what Riverpod's `select` dedupe cares about.
|
||||
static final inactive = SubscriptionStatus(
|
||||
hasActiveSubscription: false,
|
||||
status: 'inactive',
|
||||
cancelAtPeriodEnd: false,
|
||||
);
|
||||
|
||||
@override
|
||||
List<Object?> get hashParameters => [
|
||||
hasActiveSubscription,
|
||||
entitledUntil,
|
||||
status,
|
||||
subscriptionStatus,
|
||||
cancelAtPeriodEnd,
|
||||
currentPeriodEnd,
|
||||
planLabel,
|
||||
];
|
||||
}
|
||||
Reference in New Issue
Block a user