🧱 Add basis of tencent rtc
This commit is contained in:
16
lib/database/database.native.dart
Normal file
16
lib/database/database.native.dart
Normal file
@ -0,0 +1,16 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:drift/drift.dart';
|
||||
import 'package:drift/native.dart';
|
||||
import 'package:island/database/drift_db.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
|
||||
AppDatabase constructDb() {
|
||||
final db = LazyDatabase(() async {
|
||||
final dbFolder = await getApplicationDocumentsDirectory();
|
||||
final file = File(p.join(dbFolder.path, 'solar_network_data.sqlite'));
|
||||
return NativeDatabase(file);
|
||||
});
|
||||
return AppDatabase(db);
|
||||
}
|
20
lib/database/database.web.dart
Normal file
20
lib/database/database.web.dart
Normal file
@ -0,0 +1,20 @@
|
||||
import 'package:drift/drift.dart';
|
||||
import 'package:drift/wasm.dart';
|
||||
import 'package:island/database/drift_db.dart';
|
||||
|
||||
AppDatabase constructDb() {
|
||||
return AppDatabase(connectOnWeb());
|
||||
}
|
||||
|
||||
DatabaseConnection connectOnWeb() {
|
||||
return DatabaseConnection.delayed(
|
||||
Future(() async {
|
||||
final result = await WasmDatabase.open(
|
||||
databaseName: 'solar_network_data',
|
||||
sqlite3Uri: Uri.parse('sqlite3.wasm'),
|
||||
driftWorkerUri: Uri.parse('drift_worker.dart.js'),
|
||||
);
|
||||
return result.resolvedExecutor;
|
||||
}),
|
||||
);
|
||||
}
|
@ -1,17 +1,13 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'package:drift/drift.dart';
|
||||
import 'package:drift/native.dart';
|
||||
import 'package:island/database/message.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
|
||||
part 'drift_db.g.dart';
|
||||
|
||||
// Define the database
|
||||
@DriftDatabase(tables: [ChatMessages])
|
||||
class AppDatabase extends _$AppDatabase {
|
||||
AppDatabase() : super(_openConnection());
|
||||
AppDatabase(super.e);
|
||||
|
||||
@override
|
||||
int get schemaVersion => 1;
|
||||
@ -75,12 +71,3 @@ class AppDatabase extends _$AppDatabase {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Helper to open the database connection
|
||||
LazyDatabase _openConnection() {
|
||||
return LazyDatabase(() async {
|
||||
final dbFolder = await getApplicationDocumentsDirectory();
|
||||
final file = File(p.join(dbFolder.path, 'solar_network_data.sqlite'));
|
||||
return NativeDatabase(file);
|
||||
});
|
||||
}
|
||||
|
@ -124,3 +124,14 @@ abstract class MessageSyncResponse with _$MessageSyncResponse {
|
||||
factory MessageSyncResponse.fromJson(Map<String, dynamic> json) =>
|
||||
_$MessageSyncResponseFromJson(json);
|
||||
}
|
||||
|
||||
@freezed
|
||||
abstract class ChatRealtimeJoinResponse with _$ChatRealtimeJoinResponse {
|
||||
const factory ChatRealtimeJoinResponse({
|
||||
required String token,
|
||||
required Map<String, dynamic> config,
|
||||
}) = _ChatRealtimeJoinResponse;
|
||||
|
||||
factory ChatRealtimeJoinResponse.fromJson(Map<String, dynamic> json) =>
|
||||
_$ChatRealtimeJoinResponseFromJson(json);
|
||||
}
|
||||
|
@ -1179,6 +1179,148 @@ as DateTime,
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// @nodoc
|
||||
mixin _$ChatRealtimeJoinResponse {
|
||||
|
||||
String get token; Map<String, dynamic> get config;
|
||||
/// Create a copy of ChatRealtimeJoinResponse
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
$ChatRealtimeJoinResponseCopyWith<ChatRealtimeJoinResponse> get copyWith => _$ChatRealtimeJoinResponseCopyWithImpl<ChatRealtimeJoinResponse>(this as ChatRealtimeJoinResponse, _$identity);
|
||||
|
||||
/// Serializes this ChatRealtimeJoinResponse to a JSON map.
|
||||
Map<String, dynamic> toJson();
|
||||
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is ChatRealtimeJoinResponse&&(identical(other.token, token) || other.token == token)&&const DeepCollectionEquality().equals(other.config, config));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,token,const DeepCollectionEquality().hash(config));
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ChatRealtimeJoinResponse(token: $token, config: $config)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class $ChatRealtimeJoinResponseCopyWith<$Res> {
|
||||
factory $ChatRealtimeJoinResponseCopyWith(ChatRealtimeJoinResponse value, $Res Function(ChatRealtimeJoinResponse) _then) = _$ChatRealtimeJoinResponseCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
String token, Map<String, dynamic> config
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class _$ChatRealtimeJoinResponseCopyWithImpl<$Res>
|
||||
implements $ChatRealtimeJoinResponseCopyWith<$Res> {
|
||||
_$ChatRealtimeJoinResponseCopyWithImpl(this._self, this._then);
|
||||
|
||||
final ChatRealtimeJoinResponse _self;
|
||||
final $Res Function(ChatRealtimeJoinResponse) _then;
|
||||
|
||||
/// Create a copy of ChatRealtimeJoinResponse
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? token = null,Object? config = null,}) {
|
||||
return _then(_self.copyWith(
|
||||
token: null == token ? _self.token : token // ignore: cast_nullable_to_non_nullable
|
||||
as String,config: null == config ? _self.config : config // ignore: cast_nullable_to_non_nullable
|
||||
as Map<String, dynamic>,
|
||||
));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
|
||||
class _ChatRealtimeJoinResponse implements ChatRealtimeJoinResponse {
|
||||
const _ChatRealtimeJoinResponse({required this.token, required final Map<String, dynamic> config}): _config = config;
|
||||
factory _ChatRealtimeJoinResponse.fromJson(Map<String, dynamic> json) => _$ChatRealtimeJoinResponseFromJson(json);
|
||||
|
||||
@override final String token;
|
||||
final Map<String, dynamic> _config;
|
||||
@override Map<String, dynamic> get config {
|
||||
if (_config is EqualUnmodifiableMapView) return _config;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableMapView(_config);
|
||||
}
|
||||
|
||||
|
||||
/// Create a copy of ChatRealtimeJoinResponse
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
_$ChatRealtimeJoinResponseCopyWith<_ChatRealtimeJoinResponse> get copyWith => __$ChatRealtimeJoinResponseCopyWithImpl<_ChatRealtimeJoinResponse>(this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$ChatRealtimeJoinResponseToJson(this, );
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _ChatRealtimeJoinResponse&&(identical(other.token, token) || other.token == token)&&const DeepCollectionEquality().equals(other._config, _config));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,token,const DeepCollectionEquality().hash(_config));
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ChatRealtimeJoinResponse(token: $token, config: $config)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class _$ChatRealtimeJoinResponseCopyWith<$Res> implements $ChatRealtimeJoinResponseCopyWith<$Res> {
|
||||
factory _$ChatRealtimeJoinResponseCopyWith(_ChatRealtimeJoinResponse value, $Res Function(_ChatRealtimeJoinResponse) _then) = __$ChatRealtimeJoinResponseCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
String token, Map<String, dynamic> config
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class __$ChatRealtimeJoinResponseCopyWithImpl<$Res>
|
||||
implements _$ChatRealtimeJoinResponseCopyWith<$Res> {
|
||||
__$ChatRealtimeJoinResponseCopyWithImpl(this._self, this._then);
|
||||
|
||||
final _ChatRealtimeJoinResponse _self;
|
||||
final $Res Function(_ChatRealtimeJoinResponse) _then;
|
||||
|
||||
/// Create a copy of ChatRealtimeJoinResponse
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? token = null,Object? config = null,}) {
|
||||
return _then(_ChatRealtimeJoinResponse(
|
||||
token: null == token ? _self.token : token // ignore: cast_nullable_to_non_nullable
|
||||
as String,config: null == config ? _self._config : config // ignore: cast_nullable_to_non_nullable
|
||||
as Map<String, dynamic>,
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// dart format on
|
||||
|
@ -223,3 +223,14 @@ Map<String, dynamic> _$MessageSyncResponseToJson(
|
||||
'changes': instance.changes.map((e) => e.toJson()).toList(),
|
||||
'current_timestamp': instance.currentTimestamp.toIso8601String(),
|
||||
};
|
||||
|
||||
_ChatRealtimeJoinResponse _$ChatRealtimeJoinResponseFromJson(
|
||||
Map<String, dynamic> json,
|
||||
) => _ChatRealtimeJoinResponse(
|
||||
token: json['token'] as String,
|
||||
config: json['config'] as Map<String, dynamic>,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$ChatRealtimeJoinResponseToJson(
|
||||
_ChatRealtimeJoinResponse instance,
|
||||
) => <String, dynamic>{'token': instance.token, 'config': instance.config};
|
||||
|
11
lib/pods/database.dart
Normal file
11
lib/pods/database.dart
Normal file
@ -0,0 +1,11 @@
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:island/database/drift_db.dart';
|
||||
|
||||
import 'package:island/database/database.native.dart'
|
||||
if (dart.library.html) 'package:island/database/database.web.dart';
|
||||
|
||||
final databaseProvider = Provider<AppDatabase>((ref) {
|
||||
final db = constructDb();
|
||||
ref.onDispose(() => db.close());
|
||||
return db;
|
||||
});
|
@ -2,16 +2,10 @@ import 'dart:io';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:island/database/drift_db.dart';
|
||||
import 'package:island/pods/database.dart';
|
||||
import 'package:path/path.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
|
||||
final databaseProvider = Provider<AppDatabase>((ref) {
|
||||
final db = AppDatabase();
|
||||
ref.onDispose(() => db.close());
|
||||
return db;
|
||||
});
|
||||
|
||||
Future<void> resetDatabase(WidgetRef ref) async {
|
||||
if (kIsWeb) return;
|
||||
|
||||
|
@ -10,11 +10,13 @@ import 'package:island/database/message_repository.dart';
|
||||
import 'package:island/models/chat.dart';
|
||||
import 'package:island/models/file.dart';
|
||||
import 'package:island/pods/config.dart';
|
||||
import 'package:island/pods/message.dart';
|
||||
import 'package:island/pods/database.dart';
|
||||
import 'package:island/pods/network.dart';
|
||||
import 'package:island/pods/userinfo.dart';
|
||||
import 'package:island/pods/websocket.dart';
|
||||
import 'package:island/route.gr.dart';
|
||||
import 'package:island/screens/posts/compose.dart';
|
||||
import 'package:island/services/rtc.dart';
|
||||
import 'package:island/widgets/alert.dart';
|
||||
import 'package:island/widgets/content/cloud_file_collection.dart';
|
||||
import 'package:island/widgets/content/cloud_files.dart';
|
||||
@ -471,6 +473,21 @@ class ChatRoomScreen extends HookConsumerWidget {
|
||||
error: (_, __) => const Text('Error'),
|
||||
),
|
||||
actions: [
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
final user = ref.watch(userInfoProvider);
|
||||
if (currentlyJoined) {
|
||||
leaveRealtimeChat(chatRoom.value!);
|
||||
} else {
|
||||
joinRealtimeChat(
|
||||
ref.watch(apiClientProvider),
|
||||
chatRoom.value!,
|
||||
user.value!,
|
||||
);
|
||||
}
|
||||
},
|
||||
icon: const Icon(Symbols.video_call),
|
||||
),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.more_vert),
|
||||
onPressed: () {
|
||||
|
51
lib/services/rtc.dart
Normal file
51
lib/services/rtc.dart
Normal file
@ -0,0 +1,51 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:island/models/chat.dart';
|
||||
import 'package:island/models/user.dart';
|
||||
import 'package:tencent_rtc_sdk/trtc_cloud.dart';
|
||||
import 'package:tencent_rtc_sdk/trtc_cloud_def.dart';
|
||||
import 'package:tencent_rtc_sdk/trtc_cloud_listener.dart';
|
||||
|
||||
Future<TRTCCloud> _getTRTCCloud() async {
|
||||
return TRTCCloud.sharedInstance();
|
||||
}
|
||||
|
||||
bool currentlyJoined = false;
|
||||
|
||||
Future<void> joinRealtimeChat(
|
||||
Dio apiClient,
|
||||
SnChatRoom chat,
|
||||
SnAccount user,
|
||||
) async {
|
||||
final resp = await apiClient.get('/chat/realtime/${chat.id}');
|
||||
final data = ChatRealtimeJoinResponse.fromJson(resp.data);
|
||||
final config = data.config;
|
||||
final cloud = await _getTRTCCloud();
|
||||
cloud.setConsoleEnabled(true);
|
||||
cloud.registerListener(
|
||||
TRTCCloudListener(
|
||||
onRemoteUserEnterRoom: (userId) {
|
||||
print('onRemoteUserEnterRoom: $userId');
|
||||
},
|
||||
onRemoteUserLeaveRoom: (userId, reason) {
|
||||
print('onRemoteUserLeaveRoom: $userId, $reason');
|
||||
},
|
||||
),
|
||||
);
|
||||
cloud.enterRoom(
|
||||
TRTCParams(
|
||||
sdkAppId: config['app_id'],
|
||||
userId: user.name,
|
||||
userSig: data.token,
|
||||
roomId: chat.id,
|
||||
role: TRTCRoleType.anchor,
|
||||
),
|
||||
TRTCAppScene.voiceChatRoom,
|
||||
);
|
||||
cloud.startLocalAudio(TRTCAudioQuality.speech);
|
||||
currentlyJoined = true;
|
||||
}
|
||||
|
||||
Future<void> leaveRealtimeChat(SnChatRoom chat) async {
|
||||
final cloud = await _getTRTCCloud();
|
||||
cloud.exitRoom();
|
||||
}
|
@ -6,6 +6,7 @@ import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:island/pods/userinfo.dart';
|
||||
import 'package:island/pods/websocket.dart';
|
||||
import 'package:island/route.dart';
|
||||
import 'package:material_symbols_icons/material_symbols_icons.dart';
|
||||
@ -258,6 +259,7 @@ class _WebSocketIndicator extends HookConsumerWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final user = ref.watch(userInfoProvider);
|
||||
final websocketState = ref.watch(websocketStateProvider);
|
||||
final indicatorHeight = MediaQuery.of(context).padding.top + 60;
|
||||
|
||||
@ -277,7 +279,10 @@ class _WebSocketIndicator extends HookConsumerWidget {
|
||||
|
||||
return AnimatedPositioned(
|
||||
duration: Duration(milliseconds: 1850),
|
||||
top: websocketState == WebSocketState.connected() ? -indicatorHeight : 0,
|
||||
top:
|
||||
!user.hasValue || websocketState == WebSocketState.connected()
|
||||
? -indicatorHeight
|
||||
: 0,
|
||||
curve: Curves.fastLinearToSlowEaseIn,
|
||||
left: 0,
|
||||
right: 0,
|
||||
|
Reference in New Issue
Block a user