feat: the app tells when sync fails, and when it is too old
Published apps can't be made to update, so the app now notices when it no longer matches the server: the server names the oldest app build it works with (instance.info's minimumAppBuild), asked before the first sync of each launch, and the sync engine's refusal of differing synced tables counts the same. Either way the app says "update the app; if it is up to date, the server needs an update" in a banner on Home and in the new Sync card in Settings, and what the person does meanwhile stays on the device. The Sync card shows where sync stands since the app started (syncing, everything synced, sync failing, with the last completion and failure) and offers Sync now. While the last sync failed, a dot sits on the Profile tab and the Settings cog. One sync that goes through clears it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
6c20c44cf5
commit
e35ce5c81d
5 changed files with 1016 additions and 18 deletions
|
|
@ -19,17 +19,20 @@ abstract class InstanceInfo
|
|||
InstanceInfo._({
|
||||
this.gitHubClientId,
|
||||
required this.sourceCodeUrl,
|
||||
this.minimumAppBuild,
|
||||
});
|
||||
|
||||
factory InstanceInfo({
|
||||
String? gitHubClientId,
|
||||
required String sourceCodeUrl,
|
||||
int? minimumAppBuild,
|
||||
}) = _InstanceInfoImpl;
|
||||
|
||||
factory InstanceInfo.fromJson(Map<String, dynamic> jsonSerialization) {
|
||||
return InstanceInfo(
|
||||
gitHubClientId: jsonSerialization['gitHubClientId'] as String?,
|
||||
sourceCodeUrl: jsonSerialization['sourceCodeUrl'] as String,
|
||||
minimumAppBuild: jsonSerialization['minimumAppBuild'] as int?,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -41,12 +44,18 @@ abstract class InstanceInfo
|
|||
/// offering it to the people who use it.
|
||||
String sourceCodeUrl;
|
||||
|
||||
/// The oldest build of the app (the number after `+` in its version) this
|
||||
/// server still works with; older apps ask to be updated. Null from servers
|
||||
/// that predate it: any build.
|
||||
int? minimumAppBuild;
|
||||
|
||||
/// Returns a shallow copy of this [InstanceInfo]
|
||||
/// with some or all fields replaced by the given arguments.
|
||||
@_is.useResult
|
||||
InstanceInfo copyWith({
|
||||
String? gitHubClientId,
|
||||
String? sourceCodeUrl,
|
||||
int? minimumAppBuild,
|
||||
});
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
|
|
@ -54,6 +63,7 @@ abstract class InstanceInfo
|
|||
'__className__': 'InstanceInfo',
|
||||
if (gitHubClientId != null) 'gitHubClientId': gitHubClientId,
|
||||
'sourceCodeUrl': sourceCodeUrl,
|
||||
if (minimumAppBuild != null) 'minimumAppBuild': minimumAppBuild,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -63,6 +73,7 @@ abstract class InstanceInfo
|
|||
'__className__': 'InstanceInfo',
|
||||
if (gitHubClientId != null) 'gitHubClientId': gitHubClientId,
|
||||
'sourceCodeUrl': sourceCodeUrl,
|
||||
if (minimumAppBuild != null) 'minimumAppBuild': minimumAppBuild,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -78,9 +89,11 @@ class _InstanceInfoImpl extends InstanceInfo {
|
|||
_InstanceInfoImpl({
|
||||
String? gitHubClientId,
|
||||
required String sourceCodeUrl,
|
||||
int? minimumAppBuild,
|
||||
}) : super._(
|
||||
gitHubClientId: gitHubClientId,
|
||||
sourceCodeUrl: sourceCodeUrl,
|
||||
minimumAppBuild: minimumAppBuild,
|
||||
);
|
||||
|
||||
/// Returns a shallow copy of this [InstanceInfo]
|
||||
|
|
@ -90,12 +103,16 @@ class _InstanceInfoImpl extends InstanceInfo {
|
|||
InstanceInfo copyWith({
|
||||
Object? gitHubClientId = _Undefined,
|
||||
String? sourceCodeUrl,
|
||||
Object? minimumAppBuild = _Undefined,
|
||||
}) {
|
||||
return InstanceInfo(
|
||||
gitHubClientId: gitHubClientId is String?
|
||||
? gitHubClientId
|
||||
: this.gitHubClientId,
|
||||
sourceCodeUrl: sourceCodeUrl ?? this.sourceCodeUrl,
|
||||
minimumAppBuild: minimumAppBuild is int?
|
||||
? minimumAppBuild
|
||||
: this.minimumAppBuild,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
10
halkyon_learn_server/lib/src/instance/app_compatibility.dart
Normal file
10
halkyon_learn_server/lib/src/instance/app_compatibility.dart
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
/// The oldest build of the app this server still works with: the number after
|
||||
/// `+` in the app's version (`halkyon_learn_flutter/pubspec.yaml`). Older apps
|
||||
/// stop syncing and ask to be updated.
|
||||
///
|
||||
/// Published apps can't be made to update, so raise it in the commit that
|
||||
/// breaks older ones (an endpoint they call changes or goes, for example), to
|
||||
/// the build of the first app that works again. A change to the synced tables
|
||||
/// needs no raise: an app whose synced tables differ from the server's finds
|
||||
/// out when it syncs.
|
||||
const minimumAppBuild = 1;
|
||||
|
|
@ -1,16 +1,18 @@
|
|||
import 'package:halkyon_learn_server/src/auth/github.dart';
|
||||
import 'package:halkyon_learn_server/src/generated/protocol.dart';
|
||||
import 'package:halkyon_learn_server/src/instance/app_compatibility.dart';
|
||||
import 'package:halkyon_learn_server/src/instance/source_code.dart';
|
||||
import 'package:serverpod/serverpod.dart';
|
||||
|
||||
/// What this instance offers. Public: the app asks before anyone is signed in.
|
||||
class InstanceEndpoint extends Endpoint {
|
||||
/// This instance's offer: GitHub sign-in only with the operator's OAuth
|
||||
/// app, and where its source is.
|
||||
/// app, where its source is, and the oldest app build it works with.
|
||||
Future<InstanceInfo> info(Session session) async => InstanceInfo(
|
||||
gitHubClientId: gitHubSignInAvailable(session.serverpod)
|
||||
? session.serverpod.getPassword('githubClientId')
|
||||
: null,
|
||||
sourceCodeUrl: sourceCodeUrl(),
|
||||
minimumAppBuild: minimumAppBuild,
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,3 +8,7 @@ fields:
|
|||
### Where the source of what this instance runs is; the licences require
|
||||
### offering it to the people who use it.
|
||||
sourceCodeUrl: String
|
||||
### The oldest build of the app (the number after `+` in its version) this
|
||||
### server still works with; older apps ask to be updated. Null from servers
|
||||
### that predate it: any build.
|
||||
minimumAppBuild: int?
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import 'package:halkyon_learn_server/src/instance/app_compatibility.dart';
|
||||
import 'package:halkyon_learn_server/src/instance/source_code.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
|
|
@ -16,5 +17,16 @@ void main() {
|
|||
|
||||
expect(info.sourceCodeUrl, upstreamSourceCodeUrl);
|
||||
});
|
||||
|
||||
test('names the oldest app build it still works with, to anyone, so an '
|
||||
'older app can say it needs an update', () async {
|
||||
final info = await endpoints.instance.info(
|
||||
sessionBuilder.copyWith(
|
||||
authentication: AuthenticationOverride.unauthenticated(),
|
||||
),
|
||||
);
|
||||
|
||||
expect(info.minimumAppBuild, minimumAppBuild);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue