Preserve Flutter mirror-os codebase
This commit is contained in:
@@ -0,0 +1,126 @@
|
||||
import 'dart:async';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:web_socket_channel/web_socket_channel.dart';
|
||||
import 'package:mirror_protocol/mirror_protocol.dart';
|
||||
|
||||
final connectionProvider =
|
||||
NotifierProvider<ConnectionNotifier, ConnectionState>(ConnectionNotifier.new);
|
||||
|
||||
final mirrorStateProvider = StateProvider<DisplayState?>((ref) => null);
|
||||
|
||||
final dashboardConfigProvider = StateProvider<DashboardLayoutConfig?>((ref) => null);
|
||||
|
||||
class ConnectionState {
|
||||
final bool connected;
|
||||
final bool connecting;
|
||||
final String? host;
|
||||
final String? error;
|
||||
final bool manualDisconnect;
|
||||
|
||||
const ConnectionState({
|
||||
this.connected = false,
|
||||
this.connecting = false,
|
||||
this.host,
|
||||
this.error,
|
||||
this.manualDisconnect = false,
|
||||
});
|
||||
|
||||
ConnectionState copyWith({
|
||||
bool? connected,
|
||||
bool? connecting,
|
||||
String? host,
|
||||
String? error,
|
||||
bool clearError = false,
|
||||
bool? manualDisconnect,
|
||||
}) =>
|
||||
ConnectionState(
|
||||
connected: connected ?? this.connected,
|
||||
connecting: connecting ?? this.connecting,
|
||||
host: host ?? this.host,
|
||||
error: clearError ? null : (error ?? this.error),
|
||||
manualDisconnect: manualDisconnect ?? this.manualDisconnect,
|
||||
);
|
||||
}
|
||||
|
||||
class ConnectionNotifier extends Notifier<ConnectionState> {
|
||||
WebSocketChannel? _channel;
|
||||
Timer? _reconnectTimer;
|
||||
|
||||
@override
|
||||
ConnectionState build() => const ConnectionState();
|
||||
|
||||
Future<void> connect(String host, {int port = 9090}) async {
|
||||
state = state.copyWith(connecting: true, clearError: true, manualDisconnect: false);
|
||||
|
||||
try {
|
||||
final uri = Uri.parse('ws://$host:$port');
|
||||
_channel = WebSocketChannel.connect(uri);
|
||||
await _channel!.ready;
|
||||
|
||||
state = state.copyWith(connected: true, connecting: false, host: host);
|
||||
|
||||
// Send registration
|
||||
_channel!.sink.add(RegisterMessage(role: 'remote').encode());
|
||||
|
||||
// Listen for messages
|
||||
_channel!.stream.listen(
|
||||
_handleMessage,
|
||||
onDone: () => _onDisconnected(),
|
||||
onError: (e) => _onDisconnected(error: e.toString()),
|
||||
);
|
||||
} catch (e) {
|
||||
state = state.copyWith(
|
||||
connected: false,
|
||||
connecting: false,
|
||||
error: 'Failed to connect: $e',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void _handleMessage(dynamic data) {
|
||||
try {
|
||||
final message = MirrorMessage.decode(data as String);
|
||||
switch (message) {
|
||||
case StateSync(state: final mirrorState):
|
||||
ref.read(mirrorStateProvider.notifier).state = mirrorState;
|
||||
case DashboardConfigResponse(layout: final layout):
|
||||
ref.read(dashboardConfigProvider.notifier).state = layout;
|
||||
case StatePatch():
|
||||
break;
|
||||
case EventMessage():
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} catch (e) {
|
||||
print('Failed to parse message: $e');
|
||||
}
|
||||
}
|
||||
|
||||
void _onDisconnected({String? error}) {
|
||||
state = state.copyWith(connected: false, error: error);
|
||||
_channel = null;
|
||||
|
||||
// Only auto-reconnect if not intentionally disconnected
|
||||
if (state.host != null && error != null) {
|
||||
_reconnectTimer?.cancel();
|
||||
_reconnectTimer = Timer(const Duration(seconds: 3), () {
|
||||
if (state.host != null && !state.connected) {
|
||||
connect(state.host!);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void send(MirrorMessage message) {
|
||||
_channel?.sink.add(message.encode());
|
||||
}
|
||||
|
||||
void disconnect() {
|
||||
_reconnectTimer?.cancel();
|
||||
_channel?.sink.close();
|
||||
_channel = null;
|
||||
state = const ConnectionState(manualDisconnect: true);
|
||||
ref.read(mirrorStateProvider.notifier).state = null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user