⚡ Improved attachments
This commit is contained in:
39
lib/platform.dart
Normal file
39
lib/platform.dart
Normal file
@ -0,0 +1,39 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
|
||||
abstract class PlatformInfo {
|
||||
static bool get isWeb => kIsWeb;
|
||||
|
||||
static bool get isLinux => !kIsWeb && Platform.isLinux;
|
||||
|
||||
static bool get isWindows => !kIsWeb && Platform.isWindows;
|
||||
|
||||
static bool get isMacOS => !kIsWeb && Platform.isMacOS;
|
||||
|
||||
static bool get isIOS => !kIsWeb && Platform.isIOS;
|
||||
|
||||
static bool get isAndroid => !kIsWeb && Platform.isAndroid;
|
||||
|
||||
static bool get isMobile => isAndroid || isIOS;
|
||||
|
||||
// Not first tier supported platform
|
||||
static bool get isBetaDesktop => isWindows || isLinux;
|
||||
|
||||
static bool get isDesktop => isLinux || isWindows || isMacOS;
|
||||
|
||||
static bool get useTouchscreen => !isMobile;
|
||||
|
||||
static bool get canCacheImage => isAndroid || isIOS || isMacOS;
|
||||
|
||||
static bool get canRecord => (isMobile || isMacOS);
|
||||
|
||||
static Future<String> getVersion() async {
|
||||
var version = kIsWeb ? 'Web' : 'Unknown';
|
||||
try {
|
||||
version = (await PackageInfo.fromPlatform()).version;
|
||||
} catch (_) {}
|
||||
return version;
|
||||
}
|
||||
}
|
@ -72,7 +72,7 @@ class AuthProvider extends GetConnect {
|
||||
String username,
|
||||
String password,
|
||||
) async {
|
||||
_cacheUserProfileResponse = null;
|
||||
_cachedUserProfileResponse = null;
|
||||
|
||||
final resp = await oauth2.resourceOwnerPasswordGrant(
|
||||
tokenEndpoint,
|
||||
@ -105,7 +105,7 @@ class AuthProvider extends GetConnect {
|
||||
}
|
||||
|
||||
void signout() {
|
||||
_cacheUserProfileResponse = null;
|
||||
_cachedUserProfileResponse = null;
|
||||
|
||||
Get.find<ChatProvider>().disconnect();
|
||||
Get.find<AccountProvider>().disconnect();
|
||||
@ -115,13 +115,13 @@ class AuthProvider extends GetConnect {
|
||||
storage.deleteAll();
|
||||
}
|
||||
|
||||
Response? _cacheUserProfileResponse;
|
||||
Response? _cachedUserProfileResponse;
|
||||
|
||||
Future<bool> get isAuthorized => storage.containsKey(key: 'auth_credentials');
|
||||
|
||||
Future<Response> getProfile({noCache = false}) async {
|
||||
if (!noCache && _cacheUserProfileResponse != null) {
|
||||
return _cacheUserProfileResponse!;
|
||||
if (!noCache && _cachedUserProfileResponse != null) {
|
||||
return _cachedUserProfileResponse!;
|
||||
}
|
||||
|
||||
final client = GetConnect(maxAuthRetries: 3);
|
||||
@ -132,7 +132,7 @@ class AuthProvider extends GetConnect {
|
||||
if (resp.statusCode != 200) {
|
||||
throw Exception(resp.bodyString);
|
||||
} else {
|
||||
_cacheUserProfileResponse = resp;
|
||||
_cachedUserProfileResponse = resp;
|
||||
}
|
||||
|
||||
return resp;
|
||||
|
@ -25,14 +25,25 @@ Future<double> calculateFileAspectRatio(File file) async {
|
||||
}
|
||||
|
||||
class AttachmentProvider extends GetConnect {
|
||||
static Map<String, String> mimetypeOverrides = {'mov': 'video/quicktime'};
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
httpClient.baseUrl = ServiceFinder.services['paperclip'];
|
||||
}
|
||||
|
||||
static Map<String, String> mimetypeOverrides = {'mov': 'video/quicktime'};
|
||||
final Map<int, Response> _cachedResponses = {};
|
||||
|
||||
Future<Response> getMetadata(int id) => get('/api/attachments/$id/meta');
|
||||
Future<Response> getMetadata(int id, {noCache = false}) async {
|
||||
if (!noCache && _cachedResponses.containsKey(id)) {
|
||||
return _cachedResponses[id]!;
|
||||
}
|
||||
|
||||
final resp = await get('/api/attachments/$id/meta');
|
||||
_cachedResponses[id] = resp;
|
||||
|
||||
return resp;
|
||||
}
|
||||
|
||||
Future<Response> createAttachment(File file, String hash, String usage,
|
||||
{double? ratio}) async {
|
||||
@ -122,4 +133,12 @@ class AttachmentProvider extends GetConnect {
|
||||
|
||||
return resp;
|
||||
}
|
||||
|
||||
void clearCache({int? id}) {
|
||||
if (id != null) {
|
||||
_cachedResponses.remove(id);
|
||||
} else {
|
||||
_cachedResponses.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -155,13 +155,32 @@ class SolianMessages extends Translations {
|
||||
'Are your sure to delete message @id? This action cannot be undone!',
|
||||
'callOngoing': 'A call is ongoing...',
|
||||
'callJoin': 'Join',
|
||||
'callResume': 'Resume',
|
||||
'callMicrophone': 'Microphone',
|
||||
'callMicrophoneDisabled': 'Microphone Disabled',
|
||||
'callMicrophoneSelect': 'Select Microphone',
|
||||
'callCamera': 'Camera',
|
||||
'callCameraDisabled': 'Camera Disabled',
|
||||
'callCameraSelect': 'Select Camera',
|
||||
'callDisconnected': 'Call has been disconnected... @reason',
|
||||
'callSpeakerSelect': 'Select Speaker',
|
||||
'callDisconnected': 'Call Disconnected... @reason',
|
||||
'callMicrophoneOn': 'Turn Microphone On',
|
||||
'callMicrophoneOff': 'Turn Microphone Off',
|
||||
'callCameraOn': 'Turn Camera On',
|
||||
'callCameraOff': 'Turn Camera Off',
|
||||
'callVideoFlip': 'Flip Video Input',
|
||||
'callSpeakerphoneToggle': 'Toggle Speakerphone Mode',
|
||||
'callScreenOn': 'Start Screen Sharing',
|
||||
'callScreenOff': 'Stop Screen Sharing',
|
||||
'callDisconnect': 'Disconnect',
|
||||
'callDisconnectCaption':
|
||||
'Are you sure you want to disconnect from this call? You can also just return to the page, and the call will continue in the background.',
|
||||
'callParticipantAction': 'Participant Actions',
|
||||
'callParticipantMicrophoneOff': 'Mute Participant',
|
||||
'callParticipantMicrophoneOn': 'Unmute Participant',
|
||||
'callParticipantVideoOff': 'Turn Off Participant Video',
|
||||
'callParticipantVideoOn': 'Turn On Participant Video',
|
||||
'callAlreadyOngoing': 'A call is already ongoing',
|
||||
},
|
||||
'zh_CN': {
|
||||
'hide': '隐藏',
|
||||
|
@ -1,4 +1,6 @@
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:solian/platform.dart';
|
||||
import 'package:solian/services.dart';
|
||||
|
||||
class AccountAvatar extends StatelessWidget {
|
||||
@ -25,16 +27,18 @@ class AccountAvatar extends StatelessWidget {
|
||||
if (!isEmpty) isEmpty = content.endsWith('/api/attachments/0');
|
||||
}
|
||||
|
||||
final url = direct
|
||||
? content
|
||||
: '${ServiceFinder.services['paperclip']}/api/attachments/$content';
|
||||
|
||||
return CircleAvatar(
|
||||
key: Key('a$content'),
|
||||
radius: radius,
|
||||
backgroundColor: bgColor,
|
||||
backgroundImage: !isEmpty
|
||||
? NetworkImage(
|
||||
direct
|
||||
? content
|
||||
: '${ServiceFinder.services['paperclip']}/api/attachments/$content',
|
||||
)
|
||||
? (PlatformInfo.canCacheImage
|
||||
? CachedNetworkImageProvider(url)
|
||||
: NetworkImage(url)) as ImageProvider<Object>?
|
||||
: null,
|
||||
child: isEmpty
|
||||
? Icon(
|
||||
|
@ -1,7 +1,9 @@
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:chewie/chewie.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:solian/models/attachment.dart';
|
||||
import 'package:solian/platform.dart';
|
||||
import 'package:solian/services.dart';
|
||||
import 'package:url_launcher/url_launcher_string.dart';
|
||||
import 'package:video_player/video_player.dart';
|
||||
@ -61,10 +63,33 @@ class _AttachmentItemState extends State<AttachmentItem> {
|
||||
child: Stack(
|
||||
fit: StackFit.expand,
|
||||
children: [
|
||||
Image.network(
|
||||
'${ServiceFinder.services['paperclip']}/api/attachments/${widget.item.id}',
|
||||
fit: widget.fit,
|
||||
),
|
||||
if (PlatformInfo.canCacheImage)
|
||||
CachedNetworkImage(
|
||||
imageUrl:
|
||||
'${ServiceFinder.services['paperclip']}/api/attachments/${widget.item.id}',
|
||||
progressIndicatorBuilder: (context, url, downloadProgress) =>
|
||||
CircularProgressIndicator(
|
||||
value: downloadProgress.progress,
|
||||
),
|
||||
fit: widget.fit,
|
||||
)
|
||||
else
|
||||
Image.network(
|
||||
'${ServiceFinder.services['paperclip']}/api/attachments/${widget.item.id}',
|
||||
fit: widget.fit,
|
||||
loadingBuilder: (BuildContext context, Widget child,
|
||||
ImageChunkEvent? loadingProgress) {
|
||||
if (loadingProgress == null) return child;
|
||||
return Center(
|
||||
child: CircularProgressIndicator(
|
||||
value: loadingProgress.expectedTotalBytes != null
|
||||
? loadingProgress.cumulativeBytesLoaded /
|
||||
loadingProgress.expectedTotalBytes!
|
||||
: null,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
if (widget.showBadge && widget.badge != null)
|
||||
Positioned(
|
||||
right: 12,
|
||||
|
@ -390,12 +390,16 @@ class _AttachmentEditingDialogState extends State<AttachmentEditingDialog> {
|
||||
: null,
|
||||
isMature: _isMature,
|
||||
);
|
||||
|
||||
Get.find<AttachmentProvider>().clearCache(id: widget.item.id);
|
||||
|
||||
setState(() => _isBusy = false);
|
||||
return Attachment.fromJson(resp.body);
|
||||
} catch (e) {
|
||||
context.showErrorDialog(e);
|
||||
return null;
|
||||
} finally {
|
||||
|
||||
setState(() => _isBusy = false);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user