154 lines
4.0 KiB
Dart
154 lines
4.0 KiB
Dart
import 'dart:convert';
|
|
import 'dart:typed_data';
|
|
|
|
import 'package:dio/dio.dart';
|
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|
|
|
import '../../../common/network/dio_provider.dart';
|
|
|
|
/// 金值 `jinzhi.v1.UpgradeReply` 的 HTTP protobuf 契约。
|
|
class UpgradeInfo {
|
|
const UpgradeInfo({
|
|
required this.show,
|
|
required this.force,
|
|
required this.message,
|
|
required this.url,
|
|
required this.isLatestVersion,
|
|
required this.latestVersion,
|
|
required this.versionCode,
|
|
required this.hash,
|
|
});
|
|
|
|
final int show;
|
|
final int force;
|
|
final String message;
|
|
final String url;
|
|
final int isLatestVersion;
|
|
final String latestVersion;
|
|
final String versionCode;
|
|
final String hash;
|
|
|
|
bool get hasPublishedUpdate =>
|
|
isLatestVersion != 1 &&
|
|
url.trim().isNotEmpty &&
|
|
(latestVersion.trim().isNotEmpty || versionCode.trim().isNotEmpty);
|
|
|
|
factory UpgradeInfo.fromProtobuf(List<int> bytes) {
|
|
final fields = _ProtoReader(bytes).readFields();
|
|
return UpgradeInfo(
|
|
show: fields.integer(1),
|
|
force: fields.integer(2),
|
|
message: fields.string(3),
|
|
url: fields.string(4),
|
|
isLatestVersion: fields.integer(5),
|
|
latestVersion: fields.string(6),
|
|
versionCode: fields.string(7),
|
|
hash: fields.string(8),
|
|
);
|
|
}
|
|
}
|
|
|
|
// BLOCKED(proto): this endpoint has no authoritative generated Upgrade proto.
|
|
// Keep its existing wire contract until that source becomes available.
|
|
class UpdateApiClient {
|
|
UpdateApiClient(this._dio);
|
|
|
|
final Dio _dio;
|
|
|
|
Future<UpgradeInfo> checkUpgrade() async {
|
|
final response = await _dio.post<List<int>>(
|
|
'/sys/upgrade',
|
|
// Uint8List ensures Dio sends the empty protobuf as 0 bytes instead of
|
|
// JSON-encoding an empty Dart List as `[]` (Content-Length: 2).
|
|
data: Uint8List(0), // jinzhi.v1.GetUpgradeRequest
|
|
options: protobufOptions(),
|
|
);
|
|
return UpgradeInfo.fromProtobuf(response.data ?? const <int>[]);
|
|
}
|
|
|
|
Future<void> reportClick({
|
|
required String buildId,
|
|
required String source,
|
|
}) async {
|
|
await _dio.post<List<int>>(
|
|
'/active/upgrade/click',
|
|
data: _ProtoWriter()
|
|
.string(1, buildId)
|
|
.string(2, source)
|
|
.toBytes(), // jinzhi.v1.UpgradeClickRequest
|
|
options: protobufOptions(),
|
|
);
|
|
}
|
|
}
|
|
|
|
final updateApiClientProvider = Provider<UpdateApiClient>(
|
|
(ref) => UpdateApiClient(ref.watch(dioProvider)),
|
|
);
|
|
|
|
class _ProtoFields {
|
|
final _values = <int, Object>{};
|
|
int integer(int tag) => _values[tag] as int? ?? 0;
|
|
String string(int tag) => _values[tag] as String? ?? '';
|
|
}
|
|
|
|
class _ProtoReader {
|
|
_ProtoReader(this._bytes);
|
|
final List<int> _bytes;
|
|
var _index = 0;
|
|
|
|
_ProtoFields readFields() {
|
|
final result = _ProtoFields();
|
|
while (_index < _bytes.length) {
|
|
final key = _varint();
|
|
final tag = key >> 3;
|
|
switch (key & 7) {
|
|
case 0:
|
|
result._values[tag] = _varint();
|
|
case 2:
|
|
final length = _varint();
|
|
final end = _index + length;
|
|
if (end > _bytes.length) {
|
|
throw const FormatException('Invalid protobuf');
|
|
}
|
|
result._values[tag] = utf8.decode(_bytes.sublist(_index, end));
|
|
_index = end;
|
|
default:
|
|
throw const FormatException('Unsupported protobuf wire type');
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
int _varint() {
|
|
var value = 0;
|
|
var shift = 0;
|
|
while (_index < _bytes.length && shift < 64) {
|
|
final byte = _bytes[_index++];
|
|
value |= (byte & 0x7f) << shift;
|
|
if (byte < 0x80) return value;
|
|
shift += 7;
|
|
}
|
|
throw const FormatException('Invalid protobuf varint');
|
|
}
|
|
}
|
|
|
|
class _ProtoWriter {
|
|
final _bytes = BytesBuilder(copy: false);
|
|
_ProtoWriter string(int tag, String value) {
|
|
final bytes = utf8.encode(value);
|
|
_varint((tag << 3) | 2);
|
|
_varint(bytes.length);
|
|
_bytes.add(bytes);
|
|
return this;
|
|
}
|
|
|
|
Uint8List toBytes() => _bytes.toBytes();
|
|
void _varint(int value) {
|
|
while (value > 0x7f) {
|
|
_bytes.addByte((value & 0x7f) | 0x80);
|
|
value >>= 7;
|
|
}
|
|
_bytes.addByte(value);
|
|
}
|
|
}
|