Compare commits
3 Commits
Author | SHA1 | Date | |
---|---|---|---|
010a49251c | |||
bdc13978c3 | |||
5d8c73e468 |
@ -186,7 +186,7 @@ class MessageRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<LocalChatMessage> sendMessage(
|
Future<LocalChatMessage> sendMessage(
|
||||||
String atk,
|
String token,
|
||||||
String baseUrl,
|
String baseUrl,
|
||||||
String roomId,
|
String roomId,
|
||||||
String content,
|
String content,
|
||||||
@ -232,7 +232,7 @@ class MessageRepository {
|
|||||||
final cloudFile =
|
final cloudFile =
|
||||||
await putMediaToCloud(
|
await putMediaToCloud(
|
||||||
fileData: attachments[idx].data,
|
fileData: attachments[idx].data,
|
||||||
atk: atk,
|
atk: token,
|
||||||
baseUrl: baseUrl,
|
baseUrl: baseUrl,
|
||||||
filename: attachments[idx].data.name ?? 'Post media',
|
filename: attachments[idx].data.name ?? 'Post media',
|
||||||
mimetype:
|
mimetype:
|
||||||
|
@ -4,14 +4,11 @@ part 'auth.freezed.dart';
|
|||||||
part 'auth.g.dart';
|
part 'auth.g.dart';
|
||||||
|
|
||||||
@freezed
|
@freezed
|
||||||
sealed class AppTokenPair with _$AppTokenPair {
|
sealed class AppToken with _$AppToken {
|
||||||
const factory AppTokenPair({
|
const factory AppToken({required String token}) = _AppToken;
|
||||||
required String accessToken,
|
|
||||||
required String refreshToken,
|
|
||||||
}) = _AppTokenPair;
|
|
||||||
|
|
||||||
factory AppTokenPair.fromJson(Map<String, dynamic> json) =>
|
factory AppToken.fromJson(Map<String, dynamic> json) =>
|
||||||
_$AppTokenPairFromJson(json);
|
_$AppTokenFromJson(json);
|
||||||
}
|
}
|
||||||
|
|
||||||
@freezed
|
@freezed
|
||||||
|
@ -14,42 +14,42 @@ part of 'auth.dart';
|
|||||||
T _$identity<T>(T value) => value;
|
T _$identity<T>(T value) => value;
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
mixin _$AppTokenPair {
|
mixin _$AppToken {
|
||||||
|
|
||||||
String get accessToken; String get refreshToken;
|
String get token;
|
||||||
/// Create a copy of AppTokenPair
|
/// Create a copy of AppToken
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
@pragma('vm:prefer-inline')
|
@pragma('vm:prefer-inline')
|
||||||
$AppTokenPairCopyWith<AppTokenPair> get copyWith => _$AppTokenPairCopyWithImpl<AppTokenPair>(this as AppTokenPair, _$identity);
|
$AppTokenCopyWith<AppToken> get copyWith => _$AppTokenCopyWithImpl<AppToken>(this as AppToken, _$identity);
|
||||||
|
|
||||||
/// Serializes this AppTokenPair to a JSON map.
|
/// Serializes this AppToken to a JSON map.
|
||||||
Map<String, dynamic> toJson();
|
Map<String, dynamic> toJson();
|
||||||
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is AppTokenPair&&(identical(other.accessToken, accessToken) || other.accessToken == accessToken)&&(identical(other.refreshToken, refreshToken) || other.refreshToken == refreshToken));
|
return identical(this, other) || (other.runtimeType == runtimeType&&other is AppToken&&(identical(other.token, token) || other.token == token));
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
@override
|
@override
|
||||||
int get hashCode => Object.hash(runtimeType,accessToken,refreshToken);
|
int get hashCode => Object.hash(runtimeType,token);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'AppTokenPair(accessToken: $accessToken, refreshToken: $refreshToken)';
|
return 'AppToken(token: $token)';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
abstract mixin class $AppTokenPairCopyWith<$Res> {
|
abstract mixin class $AppTokenCopyWith<$Res> {
|
||||||
factory $AppTokenPairCopyWith(AppTokenPair value, $Res Function(AppTokenPair) _then) = _$AppTokenPairCopyWithImpl;
|
factory $AppTokenCopyWith(AppToken value, $Res Function(AppToken) _then) = _$AppTokenCopyWithImpl;
|
||||||
@useResult
|
@useResult
|
||||||
$Res call({
|
$Res call({
|
||||||
String accessToken, String refreshToken
|
String token
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@ -57,19 +57,18 @@ $Res call({
|
|||||||
|
|
||||||
}
|
}
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
class _$AppTokenPairCopyWithImpl<$Res>
|
class _$AppTokenCopyWithImpl<$Res>
|
||||||
implements $AppTokenPairCopyWith<$Res> {
|
implements $AppTokenCopyWith<$Res> {
|
||||||
_$AppTokenPairCopyWithImpl(this._self, this._then);
|
_$AppTokenCopyWithImpl(this._self, this._then);
|
||||||
|
|
||||||
final AppTokenPair _self;
|
final AppToken _self;
|
||||||
final $Res Function(AppTokenPair) _then;
|
final $Res Function(AppToken) _then;
|
||||||
|
|
||||||
/// Create a copy of AppTokenPair
|
/// Create a copy of AppToken
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@pragma('vm:prefer-inline') @override $Res call({Object? accessToken = null,Object? refreshToken = null,}) {
|
@pragma('vm:prefer-inline') @override $Res call({Object? token = null,}) {
|
||||||
return _then(_self.copyWith(
|
return _then(_self.copyWith(
|
||||||
accessToken: null == accessToken ? _self.accessToken : accessToken // ignore: cast_nullable_to_non_nullable
|
token: null == token ? _self.token : token // ignore: cast_nullable_to_non_nullable
|
||||||
as String,refreshToken: null == refreshToken ? _self.refreshToken : refreshToken // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String,
|
as String,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@ -80,47 +79,46 @@ as String,
|
|||||||
/// @nodoc
|
/// @nodoc
|
||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
|
|
||||||
class _AppTokenPair implements AppTokenPair {
|
class _AppToken implements AppToken {
|
||||||
const _AppTokenPair({required this.accessToken, required this.refreshToken});
|
const _AppToken({required this.token});
|
||||||
factory _AppTokenPair.fromJson(Map<String, dynamic> json) => _$AppTokenPairFromJson(json);
|
factory _AppToken.fromJson(Map<String, dynamic> json) => _$AppTokenFromJson(json);
|
||||||
|
|
||||||
@override final String accessToken;
|
@override final String token;
|
||||||
@override final String refreshToken;
|
|
||||||
|
|
||||||
/// Create a copy of AppTokenPair
|
/// Create a copy of AppToken
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@override @JsonKey(includeFromJson: false, includeToJson: false)
|
@override @JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
@pragma('vm:prefer-inline')
|
@pragma('vm:prefer-inline')
|
||||||
_$AppTokenPairCopyWith<_AppTokenPair> get copyWith => __$AppTokenPairCopyWithImpl<_AppTokenPair>(this, _$identity);
|
_$AppTokenCopyWith<_AppToken> get copyWith => __$AppTokenCopyWithImpl<_AppToken>(this, _$identity);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
return _$AppTokenPairToJson(this, );
|
return _$AppTokenToJson(this, );
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _AppTokenPair&&(identical(other.accessToken, accessToken) || other.accessToken == accessToken)&&(identical(other.refreshToken, refreshToken) || other.refreshToken == refreshToken));
|
return identical(this, other) || (other.runtimeType == runtimeType&&other is _AppToken&&(identical(other.token, token) || other.token == token));
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
@override
|
@override
|
||||||
int get hashCode => Object.hash(runtimeType,accessToken,refreshToken);
|
int get hashCode => Object.hash(runtimeType,token);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'AppTokenPair(accessToken: $accessToken, refreshToken: $refreshToken)';
|
return 'AppToken(token: $token)';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
abstract mixin class _$AppTokenPairCopyWith<$Res> implements $AppTokenPairCopyWith<$Res> {
|
abstract mixin class _$AppTokenCopyWith<$Res> implements $AppTokenCopyWith<$Res> {
|
||||||
factory _$AppTokenPairCopyWith(_AppTokenPair value, $Res Function(_AppTokenPair) _then) = __$AppTokenPairCopyWithImpl;
|
factory _$AppTokenCopyWith(_AppToken value, $Res Function(_AppToken) _then) = __$AppTokenCopyWithImpl;
|
||||||
@override @useResult
|
@override @useResult
|
||||||
$Res call({
|
$Res call({
|
||||||
String accessToken, String refreshToken
|
String token
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@ -128,19 +126,18 @@ $Res call({
|
|||||||
|
|
||||||
}
|
}
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
class __$AppTokenPairCopyWithImpl<$Res>
|
class __$AppTokenCopyWithImpl<$Res>
|
||||||
implements _$AppTokenPairCopyWith<$Res> {
|
implements _$AppTokenCopyWith<$Res> {
|
||||||
__$AppTokenPairCopyWithImpl(this._self, this._then);
|
__$AppTokenCopyWithImpl(this._self, this._then);
|
||||||
|
|
||||||
final _AppTokenPair _self;
|
final _AppToken _self;
|
||||||
final $Res Function(_AppTokenPair) _then;
|
final $Res Function(_AppToken) _then;
|
||||||
|
|
||||||
/// Create a copy of AppTokenPair
|
/// Create a copy of AppToken
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@override @pragma('vm:prefer-inline') $Res call({Object? accessToken = null,Object? refreshToken = null,}) {
|
@override @pragma('vm:prefer-inline') $Res call({Object? token = null,}) {
|
||||||
return _then(_AppTokenPair(
|
return _then(_AppToken(
|
||||||
accessToken: null == accessToken ? _self.accessToken : accessToken // ignore: cast_nullable_to_non_nullable
|
token: null == token ? _self.token : token // ignore: cast_nullable_to_non_nullable
|
||||||
as String,refreshToken: null == refreshToken ? _self.refreshToken : refreshToken // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String,
|
as String,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
@ -6,17 +6,12 @@ part of 'auth.dart';
|
|||||||
// JsonSerializableGenerator
|
// JsonSerializableGenerator
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
_AppTokenPair _$AppTokenPairFromJson(Map<String, dynamic> json) =>
|
_AppToken _$AppTokenFromJson(Map<String, dynamic> json) =>
|
||||||
_AppTokenPair(
|
_AppToken(token: json['token'] as String);
|
||||||
accessToken: json['access_token'] as String,
|
|
||||||
refreshToken: json['refresh_token'] as String,
|
|
||||||
);
|
|
||||||
|
|
||||||
Map<String, dynamic> _$AppTokenPairToJson(_AppTokenPair instance) =>
|
Map<String, dynamic> _$AppTokenToJson(_AppToken instance) => <String, dynamic>{
|
||||||
<String, dynamic>{
|
'token': instance.token,
|
||||||
'access_token': instance.accessToken,
|
};
|
||||||
'refresh_token': instance.refreshToken,
|
|
||||||
};
|
|
||||||
|
|
||||||
_SnAuthChallenge _$SnAuthChallengeFromJson(Map<String, dynamic> json) =>
|
_SnAuthChallenge _$SnAuthChallengeFromJson(Map<String, dynamic> json) =>
|
||||||
_SnAuthChallenge(
|
_SnAuthChallenge(
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:developer';
|
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
@ -68,16 +67,9 @@ final apiClientProvider = Provider<Dio>((ref) {
|
|||||||
RequestInterceptorHandler handler,
|
RequestInterceptorHandler handler,
|
||||||
) async {
|
) async {
|
||||||
try {
|
try {
|
||||||
final atk = await getFreshAtk(
|
final token = await getToken(ref.watch(tokenProvider));
|
||||||
ref.watch(tokenPairProvider),
|
if (token != null) {
|
||||||
ref.watch(serverUrlProvider),
|
options.headers['Authorization'] = 'AtField $token';
|
||||||
onRefreshed: (atk, rtk) {
|
|
||||||
setTokenPair(ref.watch(sharedPreferencesProvider), atk, rtk);
|
|
||||||
ref.invalidate(tokenPairProvider);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
if (atk != null) {
|
|
||||||
options.headers['Authorization'] = 'Bearer $atk';
|
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// ignore
|
// ignore
|
||||||
@ -95,105 +87,21 @@ final apiClientProvider = Provider<Dio>((ref) {
|
|||||||
return dio;
|
return dio;
|
||||||
});
|
});
|
||||||
|
|
||||||
final tokenPairProvider = Provider<AppTokenPair?>((ref) {
|
final tokenProvider = Provider<AppToken?>((ref) {
|
||||||
final prefs = ref.watch(sharedPreferencesProvider);
|
final prefs = ref.watch(sharedPreferencesProvider);
|
||||||
final tkPairString = prefs.getString(kTokenPairStoreKey);
|
final tokenString = prefs.getString(kTokenPairStoreKey);
|
||||||
if (tkPairString == null) return null;
|
if (tokenString == null) return null;
|
||||||
return AppTokenPair.fromJson(jsonDecode(tkPairString));
|
return AppToken.fromJson(jsonDecode(tokenString));
|
||||||
});
|
});
|
||||||
|
|
||||||
Future<(String, String)?> refreshToken(String baseUrl, String? rtk) async {
|
// Token refresh functionality removed as per backend changes
|
||||||
if (rtk == null) return null;
|
|
||||||
|
|
||||||
final dio = Dio();
|
Future<String?> getToken(AppToken? token) async {
|
||||||
dio.options.baseUrl = baseUrl;
|
return token?.token;
|
||||||
|
|
||||||
final resp = await dio.post(
|
|
||||||
'/auth/token',
|
|
||||||
data: {'grant_type': 'refresh_token', 'refresh_token': rtk},
|
|
||||||
);
|
|
||||||
|
|
||||||
final String atk = resp.data['access_token'];
|
|
||||||
final String nRtk = resp.data['refresh_token'];
|
|
||||||
|
|
||||||
return (atk, nRtk);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Completer<String?>? _refreshCompleter;
|
Future<void> setToken(SharedPreferences prefs, String token) async {
|
||||||
|
final appToken = AppToken(token: token);
|
||||||
Future<String?> getFreshAtk(
|
final tokenString = jsonEncode(appToken);
|
||||||
AppTokenPair? tkPair,
|
prefs.setString(kTokenPairStoreKey, tokenString);
|
||||||
String baseUrl, {
|
|
||||||
Function(String, String)? onRefreshed,
|
|
||||||
}) async {
|
|
||||||
var atk = tkPair?.accessToken;
|
|
||||||
var rtk = tkPair?.refreshToken;
|
|
||||||
|
|
||||||
if (_refreshCompleter != null) {
|
|
||||||
return await _refreshCompleter!.future;
|
|
||||||
} else {
|
|
||||||
_refreshCompleter = Completer<String?>();
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (atk != null) {
|
|
||||||
final atkParts = atk.split('.');
|
|
||||||
if (atkParts.length != 3) {
|
|
||||||
throw Exception('invalid format of access token');
|
|
||||||
}
|
|
||||||
|
|
||||||
var rawPayload = atkParts[1].replaceAll('-', '+').replaceAll('_', '/');
|
|
||||||
switch (rawPayload.length % 4) {
|
|
||||||
case 0:
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
rawPayload += '==';
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
rawPayload += '=';
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw Exception('illegal format of access token payload');
|
|
||||||
}
|
|
||||||
|
|
||||||
final b64 = utf8.fuse(base64Url);
|
|
||||||
final payload = b64.decode(rawPayload);
|
|
||||||
final exp = jsonDecode(payload)['exp'];
|
|
||||||
if (exp <= DateTime.now().millisecondsSinceEpoch ~/ 1000) {
|
|
||||||
log('[Auth] Access token need refresh, doing it at ${DateTime.now()}');
|
|
||||||
final result = await refreshToken(baseUrl, rtk);
|
|
||||||
if (result == null) {
|
|
||||||
atk = null;
|
|
||||||
} else {
|
|
||||||
onRefreshed?.call(result.$1, result.$2);
|
|
||||||
atk = result.$1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (atk != null) {
|
|
||||||
_refreshCompleter!.complete(atk);
|
|
||||||
return atk;
|
|
||||||
} else {
|
|
||||||
log('[Auth] Access token refresh failed...');
|
|
||||||
_refreshCompleter!.complete(null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
log('[Auth] Failed to authenticate user... $err');
|
|
||||||
_refreshCompleter!.completeError(err);
|
|
||||||
} finally {
|
|
||||||
_refreshCompleter = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> setTokenPair(
|
|
||||||
SharedPreferences prefs,
|
|
||||||
String atk,
|
|
||||||
String rtk,
|
|
||||||
) async {
|
|
||||||
final tkPair = AppTokenPair(accessToken: atk, refreshToken: rtk);
|
|
||||||
final tkPairString = jsonEncode(tkPair);
|
|
||||||
prefs.setString(kTokenPairStoreKey, tkPairString);
|
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ class UserInfoNotifier extends StateNotifier<AsyncValue<SnAccount?>> {
|
|||||||
|
|
||||||
Future<String?> getAccessToken() async {
|
Future<String?> getAccessToken() async {
|
||||||
final prefs = _ref.read(sharedPreferencesProvider);
|
final prefs = _ref.read(sharedPreferencesProvider);
|
||||||
return prefs.getString('dyn_user_atk');
|
return prefs.getString(kTokenPairStoreKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> fetchUser() async {
|
Future<void> fetchUser() async {
|
||||||
|
@ -54,25 +54,18 @@ class WebSocketService {
|
|||||||
_statusStreamController.sink.add(WebSocketState.connecting());
|
_statusStreamController.sink.add(WebSocketState.connecting());
|
||||||
|
|
||||||
final baseUrl = ref.watch(serverUrlProvider);
|
final baseUrl = ref.watch(serverUrlProvider);
|
||||||
final atk = await getFreshAtk(
|
final token = await getToken(ref.watch(tokenProvider));
|
||||||
ref.watch(tokenPairProvider),
|
|
||||||
baseUrl,
|
|
||||||
onRefreshed: (atk, rtk) {
|
|
||||||
setTokenPair(ref.watch(sharedPreferencesProvider), atk, rtk);
|
|
||||||
ref.invalidate(tokenPairProvider);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
final url = '$baseUrl/ws'.replaceFirst('http', 'ws');
|
final url = '$baseUrl/ws'.replaceFirst('http', 'ws');
|
||||||
|
|
||||||
log('[WebSocket] Trying connecting to $url');
|
log('[WebSocket] Trying connecting to $url');
|
||||||
try {
|
try {
|
||||||
if (kIsWeb) {
|
if (kIsWeb) {
|
||||||
_channel = WebSocketChannel.connect(Uri.parse('$url?tk=$atk'));
|
_channel = WebSocketChannel.connect(Uri.parse('$url?tk=$token'));
|
||||||
} else {
|
} else {
|
||||||
_channel = IOWebSocketChannel.connect(
|
_channel = IOWebSocketChannel.connect(
|
||||||
Uri.parse(url),
|
Uri.parse(url),
|
||||||
headers: {'Authorization': 'Bearer $atk'},
|
headers: {'Authorization': 'Bearer $token'},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
await _channel!.ready;
|
await _channel!.ready;
|
||||||
|
@ -254,8 +254,8 @@ class AccountScreen extends HookConsumerWidget {
|
|||||||
contentPadding: EdgeInsets.symmetric(horizontal: 24),
|
contentPadding: EdgeInsets.symmetric(horizontal: 24),
|
||||||
title: Text('Copy access token'),
|
title: Text('Copy access token'),
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
final tk = ref.watch(tokenPairProvider);
|
final tk = ref.watch(tokenProvider);
|
||||||
Clipboard.setData(ClipboardData(text: tk!.accessToken));
|
Clipboard.setData(ClipboardData(text: tk!.token));
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
if (kDebugMode)
|
if (kDebugMode)
|
||||||
|
@ -59,19 +59,12 @@ class UpdateProfileScreen extends HookConsumerWidget {
|
|||||||
submitting.value = true;
|
submitting.value = true;
|
||||||
try {
|
try {
|
||||||
final baseUrl = ref.watch(serverUrlProvider);
|
final baseUrl = ref.watch(serverUrlProvider);
|
||||||
final atk = await getFreshAtk(
|
final token = await getToken(ref.watch(tokenProvider));
|
||||||
ref.watch(tokenPairProvider),
|
if (token == null) throw ArgumentError('Token is null');
|
||||||
baseUrl,
|
|
||||||
onRefreshed: (atk, rtk) {
|
|
||||||
setTokenPair(ref.watch(sharedPreferencesProvider), atk, rtk);
|
|
||||||
ref.invalidate(tokenPairProvider);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
if (atk == null) throw ArgumentError('Access token is null');
|
|
||||||
final cloudFile =
|
final cloudFile =
|
||||||
await putMediaToCloud(
|
await putMediaToCloud(
|
||||||
fileData: result,
|
fileData: result,
|
||||||
atk: atk,
|
atk: token,
|
||||||
baseUrl: baseUrl,
|
baseUrl: baseUrl,
|
||||||
filename: result.name,
|
filename: result.name,
|
||||||
mimetype: result.mimeType ?? 'image/jpeg',
|
mimetype: result.mimeType ?? 'image/jpeg',
|
||||||
|
@ -142,10 +142,9 @@ class _LoginCheckScreen extends HookConsumerWidget {
|
|||||||
'/auth/token',
|
'/auth/token',
|
||||||
data: {'grant_type': 'authorization_code', 'code': result.id},
|
data: {'grant_type': 'authorization_code', 'code': result.id},
|
||||||
);
|
);
|
||||||
final atk = tokenResp.data['access_token'];
|
final token = tokenResp.data['token'];
|
||||||
final rtk = tokenResp.data['refresh_token'];
|
setToken(ref.watch(sharedPreferencesProvider), token);
|
||||||
setTokenPair(ref.watch(sharedPreferencesProvider), atk, rtk);
|
ref.invalidate(tokenProvider);
|
||||||
ref.invalidate(tokenPairProvider);
|
|
||||||
if (!context.mounted) return;
|
if (!context.mounted) return;
|
||||||
final userNotifier = ref.read(userInfoProvider.notifier);
|
final userNotifier = ref.read(userInfoProvider.notifier);
|
||||||
userNotifier.fetchUser().then((_) {
|
userNotifier.fetchUser().then((_) {
|
||||||
|
@ -522,19 +522,12 @@ class EditChatScreen extends HookConsumerWidget {
|
|||||||
submitting.value = true;
|
submitting.value = true;
|
||||||
try {
|
try {
|
||||||
final baseUrl = ref.watch(serverUrlProvider);
|
final baseUrl = ref.watch(serverUrlProvider);
|
||||||
final atk = await getFreshAtk(
|
final token = await getToken(ref.watch(tokenProvider));
|
||||||
ref.watch(tokenPairProvider),
|
if (token == null) throw ArgumentError('Token is null');
|
||||||
baseUrl,
|
|
||||||
onRefreshed: (atk, rtk) {
|
|
||||||
setTokenPair(ref.watch(sharedPreferencesProvider), atk, rtk);
|
|
||||||
ref.invalidate(tokenPairProvider);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
if (atk == null) throw ArgumentError('Access token is null');
|
|
||||||
final cloudFile =
|
final cloudFile =
|
||||||
await putMediaToCloud(
|
await putMediaToCloud(
|
||||||
fileData: result,
|
fileData: result,
|
||||||
atk: atk,
|
atk: token,
|
||||||
baseUrl: baseUrl,
|
baseUrl: baseUrl,
|
||||||
filename: result.name,
|
filename: result.name,
|
||||||
mimetype: result.mimeType ?? 'image/jpeg',
|
mimetype: result.mimeType ?? 'image/jpeg',
|
||||||
|
@ -117,19 +117,12 @@ class MessagesNotifier extends _$MessagesNotifier {
|
|||||||
messageRepositoryProvider(_roomId).future,
|
messageRepositoryProvider(_roomId).future,
|
||||||
);
|
);
|
||||||
final baseUrl = ref.read(serverUrlProvider);
|
final baseUrl = ref.read(serverUrlProvider);
|
||||||
final atk = await getFreshAtk(
|
final token = await getToken(ref.watch(tokenProvider));
|
||||||
ref.watch(tokenPairProvider),
|
if (token == null) throw ArgumentError('Access token is null');
|
||||||
baseUrl,
|
|
||||||
onRefreshed: (atk, rtk) {
|
|
||||||
setTokenPair(ref.watch(sharedPreferencesProvider), atk, rtk);
|
|
||||||
ref.invalidate(tokenPairProvider);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
if (atk == null) throw ArgumentError('Access token is null');
|
|
||||||
|
|
||||||
final currentMessages = state.value ?? [];
|
final currentMessages = state.value ?? [];
|
||||||
await repository.sendMessage(
|
await repository.sendMessage(
|
||||||
atk,
|
token,
|
||||||
baseUrl,
|
baseUrl,
|
||||||
_roomId,
|
_roomId,
|
||||||
content,
|
content,
|
||||||
|
@ -6,7 +6,7 @@ part of 'room.dart';
|
|||||||
// RiverpodGenerator
|
// RiverpodGenerator
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
String _$messagesNotifierHash() => r'71a9fc1c6d024f6203f06225384c19335b9b6f2c';
|
String _$messagesNotifierHash() => r'afc4d43f4948ec571118cef0321838a6cefc89c0';
|
||||||
|
|
||||||
/// Copied from Dart SDK
|
/// Copied from Dart SDK
|
||||||
class _SystemHash {
|
class _SystemHash {
|
||||||
|
@ -95,19 +95,12 @@ class EditPublisherScreen extends HookConsumerWidget {
|
|||||||
submitting.value = true;
|
submitting.value = true;
|
||||||
try {
|
try {
|
||||||
final baseUrl = ref.watch(serverUrlProvider);
|
final baseUrl = ref.watch(serverUrlProvider);
|
||||||
final atk = await getFreshAtk(
|
final token = await getToken(ref.watch(tokenProvider));
|
||||||
ref.watch(tokenPairProvider),
|
if (token == null) throw ArgumentError('Token is null');
|
||||||
baseUrl,
|
|
||||||
onRefreshed: (atk, rtk) {
|
|
||||||
setTokenPair(ref.watch(sharedPreferencesProvider), atk, rtk);
|
|
||||||
ref.invalidate(tokenPairProvider);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
if (atk == null) throw ArgumentError('Access token is null');
|
|
||||||
final cloudFile =
|
final cloudFile =
|
||||||
await putMediaToCloud(
|
await putMediaToCloud(
|
||||||
fileData: result,
|
fileData: result,
|
||||||
atk: atk,
|
atk: token,
|
||||||
baseUrl: baseUrl,
|
baseUrl: baseUrl,
|
||||||
filename: result.name,
|
filename: result.name,
|
||||||
mimetype: result.mimeType ?? 'image/jpeg',
|
mimetype: result.mimeType ?? 'image/jpeg',
|
||||||
|
@ -17,6 +17,7 @@ import 'package:material_symbols_icons/symbols.dart';
|
|||||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||||
import 'package:riverpod_paging_utils/riverpod_paging_utils.dart';
|
import 'package:riverpod_paging_utils/riverpod_paging_utils.dart';
|
||||||
import 'package:island/pods/network.dart';
|
import 'package:island/pods/network.dart';
|
||||||
|
import 'package:styled_widget/styled_widget.dart';
|
||||||
|
|
||||||
part 'explore.g.dart';
|
part 'explore.g.dart';
|
||||||
|
|
||||||
@ -131,10 +132,16 @@ class _ActivityListView extends HookConsumerWidget {
|
|||||||
|
|
||||||
switch (item.type) {
|
switch (item.type) {
|
||||||
case 'posts.new':
|
case 'posts.new':
|
||||||
|
case 'posts.new.replies':
|
||||||
|
final isReply = item.type == 'posts.new.replies';
|
||||||
itemWidget = PostItem(
|
itemWidget = PostItem(
|
||||||
backgroundColor:
|
backgroundColor:
|
||||||
isWideScreen(context) ? Colors.transparent : null,
|
isWideScreen(context) ? Colors.transparent : null,
|
||||||
item: SnPost.fromJson(item.data),
|
item: SnPost.fromJson(item.data),
|
||||||
|
padding:
|
||||||
|
isReply
|
||||||
|
? EdgeInsets.only(left: 16, right: 16, bottom: 16)
|
||||||
|
: null,
|
||||||
onRefresh: (_) {
|
onRefresh: (_) {
|
||||||
activitiesNotifier.forceRefresh();
|
activitiesNotifier.forceRefresh();
|
||||||
},
|
},
|
||||||
@ -145,6 +152,21 @@ class _ActivityListView extends HookConsumerWidget {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
if (isReply) {
|
||||||
|
itemWidget = Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
const Icon(Symbols.reply),
|
||||||
|
const Gap(8),
|
||||||
|
Text('Replying your post'),
|
||||||
|
],
|
||||||
|
).padding(horizontal: 20, vertical: 8),
|
||||||
|
itemWidget,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case 'accounts.check-in':
|
case 'accounts.check-in':
|
||||||
itemWidget = CheckInActivityWidget(item: item);
|
itemWidget = CheckInActivityWidget(item: item);
|
||||||
|
@ -7,7 +7,7 @@ part of 'notification.dart';
|
|||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
String _$notificationUnreadCountNotifierHash() =>
|
String _$notificationUnreadCountNotifierHash() =>
|
||||||
r'074143cf208a3afe1495be405198532a23ef77c8';
|
r'372a2cc259d7d838cd4f33a9129f7396ef31dbb9';
|
||||||
|
|
||||||
/// See also [NotificationUnreadCountNotifier].
|
/// See also [NotificationUnreadCountNotifier].
|
||||||
@ProviderFor(NotificationUnreadCountNotifier)
|
@ProviderFor(NotificationUnreadCountNotifier)
|
||||||
|
@ -125,21 +125,14 @@ class PostComposeScreen extends HookConsumerWidget {
|
|||||||
final attachment = attachments.value[index];
|
final attachment = attachments.value[index];
|
||||||
if (attachment is SnCloudFile) return;
|
if (attachment is SnCloudFile) return;
|
||||||
final baseUrl = ref.watch(serverUrlProvider);
|
final baseUrl = ref.watch(serverUrlProvider);
|
||||||
final atk = await getFreshAtk(
|
final token = await getToken(ref.watch(tokenProvider));
|
||||||
ref.watch(tokenPairProvider),
|
if (token == null) throw ArgumentError('Token is null');
|
||||||
baseUrl,
|
|
||||||
onRefreshed: (atk, rtk) {
|
|
||||||
setTokenPair(ref.watch(sharedPreferencesProvider), atk, rtk);
|
|
||||||
ref.invalidate(tokenPairProvider);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
if (atk == null) throw ArgumentError('Access token is null');
|
|
||||||
try {
|
try {
|
||||||
attachmentProgress.value = {...attachmentProgress.value, index: 0};
|
attachmentProgress.value = {...attachmentProgress.value, index: 0};
|
||||||
final cloudFile =
|
final cloudFile =
|
||||||
await putMediaToCloud(
|
await putMediaToCloud(
|
||||||
fileData: attachment.data,
|
fileData: attachment.data,
|
||||||
atk: atk,
|
atk: token,
|
||||||
baseUrl: baseUrl,
|
baseUrl: baseUrl,
|
||||||
filename: attachment.data.name ?? 'Post media',
|
filename: attachment.data.name ?? 'Post media',
|
||||||
mimetype:
|
mimetype:
|
||||||
@ -394,49 +387,32 @@ class PostComposeScreen extends HookConsumerWidget {
|
|||||||
final isWide = isWideScreen(context);
|
final isWide = isWideScreen(context);
|
||||||
return isWide
|
return isWide
|
||||||
? Wrap(
|
? Wrap(
|
||||||
spacing: 8,
|
spacing: 8,
|
||||||
runSpacing: 8,
|
runSpacing: 8,
|
||||||
children: [
|
children: [
|
||||||
for (var idx = 0; idx < attachments.value.length; idx++)
|
for (
|
||||||
SizedBox(
|
var idx = 0;
|
||||||
width: constraints.maxWidth / 2 - 4,
|
idx < attachments.value.length;
|
||||||
child: AttachmentPreview(
|
idx++
|
||||||
item: attachments.value[idx],
|
)
|
||||||
progress: attachmentProgress.value[idx],
|
SizedBox(
|
||||||
onRequestUpload: () => uploadAttachment(idx),
|
width: constraints.maxWidth / 2 - 4,
|
||||||
onDelete: () => deleteAttachment(idx),
|
child: AttachmentPreview(
|
||||||
onMove: (delta) {
|
|
||||||
if (idx + delta < 0 ||
|
|
||||||
idx + delta >= attachments.value.length) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final clone = List.of(attachments.value);
|
|
||||||
clone.insert(
|
|
||||||
idx + delta,
|
|
||||||
clone.removeAt(idx),
|
|
||||||
);
|
|
||||||
attachments.value = clone;
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
spacing: 8,
|
|
||||||
children: [
|
|
||||||
for (var idx = 0; idx < attachments.value.length; idx++)
|
|
||||||
AttachmentPreview(
|
|
||||||
item: attachments.value[idx],
|
item: attachments.value[idx],
|
||||||
progress: attachmentProgress.value[idx],
|
progress:
|
||||||
onRequestUpload: () => uploadAttachment(idx),
|
attachmentProgress.value[idx],
|
||||||
|
onRequestUpload:
|
||||||
|
() => uploadAttachment(idx),
|
||||||
onDelete: () => deleteAttachment(idx),
|
onDelete: () => deleteAttachment(idx),
|
||||||
onMove: (delta) {
|
onMove: (delta) {
|
||||||
if (idx + delta < 0 ||
|
if (idx + delta < 0 ||
|
||||||
idx + delta >= attachments.value.length) {
|
idx + delta >=
|
||||||
|
attachments.value.length) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final clone = List.of(attachments.value);
|
final clone = List.of(
|
||||||
|
attachments.value,
|
||||||
|
);
|
||||||
clone.insert(
|
clone.insert(
|
||||||
idx + delta,
|
idx + delta,
|
||||||
clone.removeAt(idx),
|
clone.removeAt(idx),
|
||||||
@ -444,8 +420,42 @@ class PostComposeScreen extends HookConsumerWidget {
|
|||||||
attachments.value = clone;
|
attachments.value = clone;
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
);
|
],
|
||||||
|
)
|
||||||
|
: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
spacing: 8,
|
||||||
|
children: [
|
||||||
|
for (
|
||||||
|
var idx = 0;
|
||||||
|
idx < attachments.value.length;
|
||||||
|
idx++
|
||||||
|
)
|
||||||
|
AttachmentPreview(
|
||||||
|
item: attachments.value[idx],
|
||||||
|
progress: attachmentProgress.value[idx],
|
||||||
|
onRequestUpload:
|
||||||
|
() => uploadAttachment(idx),
|
||||||
|
onDelete: () => deleteAttachment(idx),
|
||||||
|
onMove: (delta) {
|
||||||
|
if (idx + delta < 0 ||
|
||||||
|
idx + delta >=
|
||||||
|
attachments.value.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final clone = List.of(
|
||||||
|
attachments.value,
|
||||||
|
);
|
||||||
|
clone.insert(
|
||||||
|
idx + delta,
|
||||||
|
clone.removeAt(idx),
|
||||||
|
);
|
||||||
|
attachments.value = clone;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -145,7 +145,7 @@ class _PublisherProviderElement
|
|||||||
String get uname => (origin as PublisherProvider).uname;
|
String get uname => (origin as PublisherProvider).uname;
|
||||||
}
|
}
|
||||||
|
|
||||||
String _$publisherBadgesHash() => r'b26d8804ddc9734c453bdf76af0a9336f166542c';
|
String _$publisherBadgesHash() => r'a5781deded7e682a781ccd7854418f050438e3f4';
|
||||||
|
|
||||||
/// See also [publisherBadges].
|
/// See also [publisherBadges].
|
||||||
@ProviderFor(publisherBadges)
|
@ProviderFor(publisherBadges)
|
||||||
|
@ -211,19 +211,12 @@ class EditRealmScreen extends HookConsumerWidget {
|
|||||||
submitting.value = true;
|
submitting.value = true;
|
||||||
try {
|
try {
|
||||||
final baseUrl = ref.watch(serverUrlProvider);
|
final baseUrl = ref.watch(serverUrlProvider);
|
||||||
final atk = await getFreshAtk(
|
final token = await getToken(ref.watch(tokenProvider));
|
||||||
ref.watch(tokenPairProvider),
|
if (token == null) throw ArgumentError('Access token is null');
|
||||||
baseUrl,
|
|
||||||
onRefreshed: (atk, rtk) {
|
|
||||||
setTokenPair(ref.watch(sharedPreferencesProvider), atk, rtk);
|
|
||||||
ref.invalidate(tokenPairProvider);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
if (atk == null) throw ArgumentError('Access token is null');
|
|
||||||
final cloudFile =
|
final cloudFile =
|
||||||
await putMediaToCloud(
|
await putMediaToCloud(
|
||||||
fileData: result,
|
fileData: result,
|
||||||
atk: atk,
|
atk: token,
|
||||||
baseUrl: baseUrl,
|
baseUrl: baseUrl,
|
||||||
filename: result.name,
|
filename: result.name,
|
||||||
mimetype: result.mimeType ?? 'image/jpeg',
|
mimetype: result.mimeType ?? 'image/jpeg',
|
||||||
|
@ -43,15 +43,8 @@ class CloudFilePicker extends HookConsumerWidget {
|
|||||||
if (files.value.isEmpty) return;
|
if (files.value.isEmpty) return;
|
||||||
|
|
||||||
final baseUrl = ref.read(serverUrlProvider);
|
final baseUrl = ref.read(serverUrlProvider);
|
||||||
final atk = await getFreshAtk(
|
final token = await getToken(ref.watch(tokenProvider));
|
||||||
ref.watch(tokenPairProvider),
|
if (token == null) throw Exception("Unauthorized");
|
||||||
baseUrl,
|
|
||||||
onRefreshed: (atk, rtk) {
|
|
||||||
setTokenPair(ref.watch(sharedPreferencesProvider), atk, rtk);
|
|
||||||
ref.invalidate(tokenPairProvider);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
if (atk == null) throw Exception("Unauthorized");
|
|
||||||
|
|
||||||
List<SnCloudFile> result = List.empty(growable: true);
|
List<SnCloudFile> result = List.empty(growable: true);
|
||||||
|
|
||||||
@ -64,7 +57,7 @@ class CloudFilePicker extends HookConsumerWidget {
|
|||||||
final cloudFile =
|
final cloudFile =
|
||||||
await putMediaToCloud(
|
await putMediaToCloud(
|
||||||
fileData: file.data,
|
fileData: file.data,
|
||||||
atk: atk,
|
atk: token,
|
||||||
baseUrl: baseUrl,
|
baseUrl: baseUrl,
|
||||||
filename: file.data.name ?? 'Post media',
|
filename: file.data.name ?? 'Post media',
|
||||||
mimetype:
|
mimetype:
|
||||||
|
@ -4,7 +4,6 @@ import 'package:flutter/foundation.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
|
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:island/pods/config.dart';
|
|
||||||
import 'package:island/pods/network.dart';
|
import 'package:island/pods/network.dart';
|
||||||
import 'package:island/widgets/alert.dart';
|
import 'package:island/widgets/alert.dart';
|
||||||
import 'package:media_kit/media_kit.dart';
|
import 'package:media_kit/media_kit.dart';
|
||||||
@ -38,18 +37,10 @@ class _UniversalVideoState extends ConsumerState<UniversalVideo> {
|
|||||||
final inCacheInfo = await DefaultCacheManager().getFileFromCache(url);
|
final inCacheInfo = await DefaultCacheManager().getFileFromCache(url);
|
||||||
if (inCacheInfo == null) {
|
if (inCacheInfo == null) {
|
||||||
log('[MediaPlayer] Miss cache: $url');
|
log('[MediaPlayer] Miss cache: $url');
|
||||||
final baseUrl = ref.watch(serverUrlProvider);
|
final token = await getToken(ref.watch(tokenProvider));
|
||||||
final atk = await getFreshAtk(
|
|
||||||
ref.watch(tokenPairProvider),
|
|
||||||
baseUrl,
|
|
||||||
onRefreshed: (atk, rtk) {
|
|
||||||
setTokenPair(ref.watch(sharedPreferencesProvider), atk, rtk);
|
|
||||||
ref.invalidate(tokenPairProvider);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
final fileStream = DefaultCacheManager().getFileStream(
|
final fileStream = DefaultCacheManager().getFileStream(
|
||||||
url,
|
url,
|
||||||
headers: {'Authorization': 'Bearer $atk'},
|
headers: {'Authorization': 'Bearer $token'},
|
||||||
withProgress: true,
|
withProgress: true,
|
||||||
);
|
);
|
||||||
await for (var fileInfo in fileStream) {
|
await for (var fileInfo in fileStream) {
|
||||||
|
@ -16,7 +16,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev
|
|||||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||||
# In Windows, build-name is used as the major, minor, and patch parts
|
# In Windows, build-name is used as the major, minor, and patch parts
|
||||||
# of the product and file versions while build-number is used as the build suffix.
|
# of the product and file versions while build-number is used as the build suffix.
|
||||||
version: 3.0.0+95
|
version: 3.0.0+96
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ^3.7.2
|
sdk: ^3.7.2
|
||||||
|
255
web/index.html
255
web/index.html
@ -1,4 +1,6 @@
|
|||||||
<!DOCTYPE html><html><head>
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
<!--
|
<!--
|
||||||
If you are serving your web app in a path other than the root, change the
|
If you are serving your web app in a path other than the root, change the
|
||||||
href value below to reflect the base path you are serving from.
|
href value below to reflect the base path you are serving from.
|
||||||
@ -12,23 +14,26 @@
|
|||||||
This is a placeholder for base href that will be replaced by the value of
|
This is a placeholder for base href that will be replaced by the value of
|
||||||
the `--base-href` argument provided to `flutter build`.
|
the `--base-href` argument provided to `flutter build`.
|
||||||
-->
|
-->
|
||||||
<base href="$FLUTTER_BASE_HREF">
|
<base href="$FLUTTER_BASE_HREF" />
|
||||||
|
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8" />
|
||||||
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
|
<meta content="IE=Edge" http-equiv="X-UA-Compatible" />
|
||||||
<meta name="description" content="The Solar Network, an open-source social network.">
|
<meta
|
||||||
|
name="description"
|
||||||
|
content="The Solar Network, an open-source social network."
|
||||||
|
/>
|
||||||
|
|
||||||
<!-- iOS meta tags & icons -->
|
<!-- iOS meta tags & icons -->
|
||||||
<meta name="mobile-web-app-capable" content="yes">
|
<meta name="mobile-web-app-capable" content="yes" />
|
||||||
<meta name="apple-mobile-web-app-status-bar-style" content="black">
|
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
|
||||||
<meta name="apple-mobile-web-app-title" content="Solar Network">
|
<meta name="apple-mobile-web-app-title" content="Solar Network" />
|
||||||
<link rel="apple-touch-icon" href="icons/Icon-192.png">
|
<link rel="apple-touch-icon" href="icons/Icon-192.png" />
|
||||||
|
|
||||||
<!-- Favicon -->
|
<!-- Favicon -->
|
||||||
<link rel="icon" type="image/png" href="favicon.png">
|
<link rel="icon" type="image/png" href="favicon.png" />
|
||||||
|
|
||||||
<title>Solar Network</title>
|
<title>Solar Network</title>
|
||||||
<link rel="manifest" href="manifest.json">
|
<link rel="manifest" href="manifest.json" />
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
@import url("https://fonts.googleapis.com/css2?family=Rubik:ital,wght@0,300..900;1,300..900&display=swap");
|
@import url("https://fonts.googleapis.com/css2?family=Rubik:ital,wght@0,300..900;1,300..900&display=swap");
|
||||||
@ -77,7 +82,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.swal-button--confirm {
|
.swal-button--confirm {
|
||||||
background-color: #6750A4;
|
background-color: #6750a4;
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,7 +92,7 @@
|
|||||||
|
|
||||||
.swal-button--cancel {
|
.swal-button--cancel {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
color: #6750A4;
|
color: #6750a4;
|
||||||
}
|
}
|
||||||
|
|
||||||
.swal-button--cancel:hover {
|
.swal-button--cancel:hover {
|
||||||
@ -95,33 +100,33 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.swal-icon {
|
.swal-icon {
|
||||||
border-color: #6750A4;
|
border-color: #6750a4;
|
||||||
margin: 12px auto;
|
margin: 12px auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.swal-icon--success__line {
|
.swal-icon--success__line {
|
||||||
background-color: #6750A4;
|
background-color: #6750a4;
|
||||||
}
|
}
|
||||||
|
|
||||||
.swal-icon--success__ring {
|
.swal-icon--success__ring {
|
||||||
border-color: #6750A4;
|
border-color: #6750a4;
|
||||||
}
|
}
|
||||||
|
|
||||||
.swal-icon--warning {
|
.swal-icon--warning {
|
||||||
border-color: #F2B824;
|
border-color: #f2b824;
|
||||||
}
|
}
|
||||||
|
|
||||||
.swal-icon--warning__body,
|
.swal-icon--warning__body,
|
||||||
.swal-icon--warning__dot {
|
.swal-icon--warning__dot {
|
||||||
background-color: #F2B824;
|
background-color: #f2b824;
|
||||||
}
|
}
|
||||||
|
|
||||||
.swal-icon--error {
|
.swal-icon--error {
|
||||||
border-color: #DC362E;
|
border-color: #dc362e;
|
||||||
}
|
}
|
||||||
|
|
||||||
.swal-icon--error__line {
|
.swal-icon--error__line {
|
||||||
background-color: #DC362E;
|
background-color: #dc362e;
|
||||||
}
|
}
|
||||||
|
|
||||||
.swal-footer {
|
.swal-footer {
|
||||||
@ -130,92 +135,144 @@
|
|||||||
border: none;
|
border: none;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<style id="splash-screen-style">
|
<style id="splash-screen-style">
|
||||||
html {
|
html {
|
||||||
height: 100%
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
|
||||||
margin: 0;
|
|
||||||
min-height: 100%;
|
|
||||||
background-color: #ffffff;
|
|
||||||
background-size: 100% 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.center {
|
|
||||||
margin: 0;
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
left: 50%;
|
|
||||||
-ms-transform: translate(-50%, -50%);
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.contain {
|
|
||||||
display:block;
|
|
||||||
width:100%; height:100%;
|
|
||||||
object-fit: contain;
|
|
||||||
}
|
|
||||||
|
|
||||||
.stretch {
|
|
||||||
display:block;
|
|
||||||
width:100%; height:100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cover {
|
|
||||||
display:block;
|
|
||||||
width:100%; height:100%;
|
|
||||||
object-fit: cover;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bottom {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 0;
|
|
||||||
left: 50%;
|
|
||||||
-ms-transform: translate(-50%, 0);
|
|
||||||
transform: translate(-50%, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
.bottomLeft {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bottomRight {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 0;
|
|
||||||
right: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
|
||||||
body {
|
body {
|
||||||
background-color: #121212;
|
margin: 0;
|
||||||
}
|
min-height: 100%;
|
||||||
}
|
background-color: #ffffff;
|
||||||
</style>
|
background-size: 100% 100%;
|
||||||
<script id="splash-screen-script">
|
}
|
||||||
function removeSplashFromWeb() {
|
|
||||||
document.getElementById("splash")?.remove();
|
.center {
|
||||||
document.getElementById("splash-branding")?.remove();
|
margin: 0;
|
||||||
document.body.style.background = "transparent";
|
position: absolute;
|
||||||
}
|
top: 50%;
|
||||||
</script>
|
left: 50%;
|
||||||
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport">
|
-ms-transform: translate(-50%, -50%);
|
||||||
</head>
|
transform: translate(-50%, -50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.contain {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: contain;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stretch {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cover {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bottom {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 50%;
|
||||||
|
-ms-transform: translate(-50%, 0);
|
||||||
|
transform: translate(-50%, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bottomLeft {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bottomRight {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
body {
|
||||||
|
background-color: #121212;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<script id="splash-screen-script">
|
||||||
|
function removeSplashFromWeb() {
|
||||||
|
document.getElementById("splash")?.remove();
|
||||||
|
document.getElementById("splash-branding")?.remove();
|
||||||
|
document.body.style.background = "transparent";
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<meta
|
||||||
|
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"
|
||||||
|
name="viewport"
|
||||||
|
/>
|
||||||
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<picture id="splash">
|
<picture id="splash">
|
||||||
<source srcset="splash/img/light-1x.png 1x, splash/img/light-2x.png 2x, splash/img/light-3x.png 3x, splash/img/light-4x.png 4x" media="(prefers-color-scheme: light)">
|
<source
|
||||||
<source srcset="splash/img/dark-1x.png 1x, splash/img/dark-2x.png 2x, splash/img/dark-3x.png 3x, splash/img/dark-4x.png 4x" media="(prefers-color-scheme: dark)">
|
srcset="
|
||||||
<img class="center" aria-hidden="true" src="splash/img/light-1x.png" alt="">
|
splash/img/light-1x.png 1x,
|
||||||
</picture>
|
splash/img/light-2x.png 2x,
|
||||||
<script src="https://unpkg.com/sweetalert@2.1.2/dist/sweetalert.min.js" async=""></script>
|
splash/img/light-3x.png 3x,
|
||||||
<script src="flutter_bootstrap.js" async=""></script>
|
splash/img/light-4x.png 4x
|
||||||
|
"
|
||||||
|
media="(prefers-color-scheme: light)"
|
||||||
|
/>
|
||||||
|
<source
|
||||||
|
srcset="
|
||||||
|
splash/img/dark-1x.png 1x,
|
||||||
|
splash/img/dark-2x.png 2x,
|
||||||
|
splash/img/dark-3x.png 3x,
|
||||||
|
splash/img/dark-4x.png 4x
|
||||||
|
"
|
||||||
|
media="(prefers-color-scheme: dark)"
|
||||||
|
/>
|
||||||
|
<img
|
||||||
|
class="center"
|
||||||
|
aria-hidden="true"
|
||||||
|
src="splash/img/light-1x.png"
|
||||||
|
alt=""
|
||||||
|
/>
|
||||||
|
</picture>
|
||||||
|
<script
|
||||||
|
src="https://unpkg.com/sweetalert@2.1.2/dist/sweetalert.min.js"
|
||||||
|
async=""
|
||||||
|
></script>
|
||||||
<script>
|
<script>
|
||||||
document.oncontextmenu = (evt) => evt.preventDefault();
|
document.oncontextmenu = (evt) => evt.preventDefault();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
{{flutter_js}}
|
||||||
|
{{flutter_build_config}}
|
||||||
|
|
||||||
|
const searchParams = new URLSearchParams(window.location.search);
|
||||||
|
const renderer = searchParams.get("renderer");
|
||||||
|
let cdn = searchParams.get("cdn");
|
||||||
|
|
||||||
|
if (cdn) {
|
||||||
|
localStorage.setItem("sn-web-canvaskit-cdn", cdn);
|
||||||
|
} else {
|
||||||
|
const storagedCdn = localStorage.getItem("sn-web-canvaskit-cdn");
|
||||||
|
cdn = storagedCdn ?? "com";
|
||||||
|
}
|
||||||
|
|
||||||
|
_flutter.loader.load({
|
||||||
|
config: {
|
||||||
|
renderer: renderer ?? "canvaskit",
|
||||||
|
canvasKitVariant: "full",
|
||||||
|
canvasKitBaseUrl: `https://www.gstatic.${cdn}/flutter-canvaskit/f73bfc4522dd0bc87bbcdb4bb3088082755c5e87`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
</body></html>
|
|
Reference in New Issue
Block a user