Merge branch 'soru/moor' into 'master'
Soru/moor See merge request ChristianPauly/fluffychat-flutter!48
This commit is contained in:
commit
18375635d1
|
@ -10,3 +10,4 @@ analyzer:
|
||||||
todo: ignore
|
todo: ignore
|
||||||
exclude:
|
exclude:
|
||||||
- lib/generated_plugin_registrant.dart
|
- lib/generated_plugin_registrant.dart
|
||||||
|
- lib/l10n/*.dart
|
|
@ -32,7 +32,7 @@ class _AudioPlayerState extends State<AudioPlayer> {
|
||||||
StreamSubscription soundSubscription;
|
StreamSubscription soundSubscription;
|
||||||
Uint8List audioFile;
|
Uint8List audioFile;
|
||||||
|
|
||||||
String statusText = "00:00";
|
String statusText = '00:00';
|
||||||
double currentPosition = 0;
|
double currentPosition = 0;
|
||||||
double maxPosition = 0;
|
double maxPosition = 0;
|
||||||
|
|
||||||
|
@ -45,7 +45,7 @@ class _AudioPlayerState extends State<AudioPlayer> {
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
_downloadAction() async {
|
Future<void> _downloadAction() async {
|
||||||
if (status != AudioPlayerStatus.NOT_DOWNLOADED) return;
|
if (status != AudioPlayerStatus.NOT_DOWNLOADED) return;
|
||||||
setState(() => status = AudioPlayerStatus.DOWNLOADING);
|
setState(() => status = AudioPlayerStatus.DOWNLOADING);
|
||||||
final matrixFile = await SimpleDialogs(context)
|
final matrixFile = await SimpleDialogs(context)
|
||||||
|
@ -57,7 +57,7 @@ class _AudioPlayerState extends State<AudioPlayer> {
|
||||||
_playAction();
|
_playAction();
|
||||||
}
|
}
|
||||||
|
|
||||||
_playAction() async {
|
void _playAction() async {
|
||||||
if (AudioPlayer.currentId != widget.event.eventId) {
|
if (AudioPlayer.currentId != widget.event.eventId) {
|
||||||
if (AudioPlayer.currentId != null) {
|
if (AudioPlayer.currentId != null) {
|
||||||
if (flutterSound.audioState != t_AUDIO_STATE.IS_STOPPED) {
|
if (flutterSound.audioState != t_AUDIO_STATE.IS_STOPPED) {
|
||||||
|
@ -84,16 +84,16 @@ class _AudioPlayerState extends State<AudioPlayer> {
|
||||||
soundSubscription ??= flutterSound.onPlayerStateChanged.listen((e) {
|
soundSubscription ??= flutterSound.onPlayerStateChanged.listen((e) {
|
||||||
if (AudioPlayer.currentId != widget.event.eventId) {
|
if (AudioPlayer.currentId != widget.event.eventId) {
|
||||||
soundSubscription?.cancel()?.then((f) => soundSubscription = null);
|
soundSubscription?.cancel()?.then((f) => soundSubscription = null);
|
||||||
this.setState(() {
|
setState(() {
|
||||||
currentPosition = 0;
|
currentPosition = 0;
|
||||||
statusText = "00:00";
|
statusText = '00:00';
|
||||||
});
|
});
|
||||||
AudioPlayer.currentId = null;
|
AudioPlayer.currentId = null;
|
||||||
} else if (e != null) {
|
} else if (e != null) {
|
||||||
DateTime date =
|
var date =
|
||||||
DateTime.fromMillisecondsSinceEpoch(e.currentPosition.toInt());
|
DateTime.fromMillisecondsSinceEpoch(e.currentPosition.toInt());
|
||||||
String txt = DateFormat('mm:ss', 'en_US').format(date);
|
var txt = DateFormat('mm:ss', 'en_US').format(date);
|
||||||
this.setState(() {
|
setState(() {
|
||||||
maxPosition = e.duration;
|
maxPosition = e.duration;
|
||||||
currentPosition = e.currentPosition;
|
currentPosition = e.currentPosition;
|
||||||
statusText = txt;
|
statusText = txt;
|
||||||
|
|
|
@ -23,13 +23,14 @@ class Avatar extends StatelessWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final String src = mxContent?.getThumbnail(
|
var thumbnail = mxContent?.getThumbnail(
|
||||||
Matrix.of(context).client,
|
Matrix.of(context).client,
|
||||||
width: size * MediaQuery.of(context).devicePixelRatio,
|
width: size * MediaQuery.of(context).devicePixelRatio,
|
||||||
height: size * MediaQuery.of(context).devicePixelRatio,
|
height: size * MediaQuery.of(context).devicePixelRatio,
|
||||||
method: ThumbnailMethod.scale,
|
method: ThumbnailMethod.scale,
|
||||||
);
|
);
|
||||||
String fallbackLetters = "@";
|
final src = thumbnail;
|
||||||
|
var fallbackLetters = '@';
|
||||||
if ((name?.length ?? 0) >= 2) {
|
if ((name?.length ?? 0) >= 2) {
|
||||||
fallbackLetters = name.substring(0, 2);
|
fallbackLetters = name.substring(0, 2);
|
||||||
} else if ((name?.length ?? 0) == 1) {
|
} else if ((name?.length ?? 0) == 1) {
|
||||||
|
|
|
@ -48,26 +48,26 @@ class _ChatSettingsPopupMenuState extends State<ChatSettingsPopupMenu> {
|
||||||
.client
|
.client
|
||||||
.onUserEvent
|
.onUserEvent
|
||||||
.stream
|
.stream
|
||||||
.where((u) => u.type == 'account_data' && u.eventType == "m.push_rules")
|
.where((u) => u.type == 'account_data' && u.eventType == 'm.push_rules')
|
||||||
.listen(
|
.listen(
|
||||||
(u) => setState(() => null),
|
(u) => setState(() => null),
|
||||||
);
|
);
|
||||||
List<PopupMenuEntry<String>> items = <PopupMenuEntry<String>>[
|
var items = <PopupMenuEntry<String>>[
|
||||||
widget.room.pushRuleState == PushRuleState.notify
|
widget.room.pushRuleState == PushRuleState.notify
|
||||||
? PopupMenuItem<String>(
|
? PopupMenuItem<String>(
|
||||||
value: "mute",
|
value: 'mute',
|
||||||
child: Text(L10n.of(context).muteChat),
|
child: Text(L10n.of(context).muteChat),
|
||||||
)
|
)
|
||||||
: PopupMenuItem<String>(
|
: PopupMenuItem<String>(
|
||||||
value: "unmute",
|
value: 'unmute',
|
||||||
child: Text(L10n.of(context).unmuteChat),
|
child: Text(L10n.of(context).unmuteChat),
|
||||||
),
|
),
|
||||||
PopupMenuItem<String>(
|
PopupMenuItem<String>(
|
||||||
value: "call",
|
value: 'call',
|
||||||
child: Text(L10n.of(context).videoCall),
|
child: Text(L10n.of(context).videoCall),
|
||||||
),
|
),
|
||||||
PopupMenuItem<String>(
|
PopupMenuItem<String>(
|
||||||
value: "leave",
|
value: 'leave',
|
||||||
child: Text(L10n.of(context).leave),
|
child: Text(L10n.of(context).leave),
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
|
@ -75,7 +75,7 @@ class _ChatSettingsPopupMenuState extends State<ChatSettingsPopupMenu> {
|
||||||
items.insert(
|
items.insert(
|
||||||
0,
|
0,
|
||||||
PopupMenuItem<String>(
|
PopupMenuItem<String>(
|
||||||
value: "details",
|
value: 'details',
|
||||||
child: Text(L10n.of(context).chatDetails),
|
child: Text(L10n.of(context).chatDetails),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -83,8 +83,8 @@ class _ChatSettingsPopupMenuState extends State<ChatSettingsPopupMenu> {
|
||||||
return PopupMenuButton(
|
return PopupMenuButton(
|
||||||
onSelected: (String choice) async {
|
onSelected: (String choice) async {
|
||||||
switch (choice) {
|
switch (choice) {
|
||||||
case "leave":
|
case 'leave':
|
||||||
bool confirmed = await SimpleDialogs(context).askConfirmation();
|
var confirmed = await SimpleDialogs(context).askConfirmation();
|
||||||
if (confirmed) {
|
if (confirmed) {
|
||||||
final success = await SimpleDialogs(context)
|
final success = await SimpleDialogs(context)
|
||||||
.tryRequestWithLoadingDialog(widget.room.leave());
|
.tryRequestWithLoadingDialog(widget.room.leave());
|
||||||
|
@ -95,18 +95,18 @@ class _ChatSettingsPopupMenuState extends State<ChatSettingsPopupMenu> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "mute":
|
case 'mute':
|
||||||
await SimpleDialogs(context).tryRequestWithLoadingDialog(
|
await SimpleDialogs(context).tryRequestWithLoadingDialog(
|
||||||
widget.room.setPushRuleState(PushRuleState.mentions_only));
|
widget.room.setPushRuleState(PushRuleState.mentions_only));
|
||||||
break;
|
break;
|
||||||
case "unmute":
|
case 'unmute':
|
||||||
await SimpleDialogs(context).tryRequestWithLoadingDialog(
|
await SimpleDialogs(context).tryRequestWithLoadingDialog(
|
||||||
widget.room.setPushRuleState(PushRuleState.notify));
|
widget.room.setPushRuleState(PushRuleState.notify));
|
||||||
break;
|
break;
|
||||||
case "call":
|
case 'call':
|
||||||
startCallAction(context);
|
startCallAction(context);
|
||||||
break;
|
break;
|
||||||
case "details":
|
case 'details':
|
||||||
await Navigator.of(context).push(
|
await Navigator.of(context).push(
|
||||||
AppRoute.defaultRoute(
|
AppRoute.defaultRoute(
|
||||||
context,
|
context,
|
||||||
|
|
|
@ -23,9 +23,9 @@ class ContentBanner extends StatelessWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final mediaQuery = MediaQuery.of(context);
|
final mediaQuery = MediaQuery.of(context);
|
||||||
final int bannerSize =
|
final bannerSize =
|
||||||
(mediaQuery.size.width * mediaQuery.devicePixelRatio).toInt();
|
(mediaQuery.size.width * mediaQuery.devicePixelRatio).toInt();
|
||||||
final String src = mxContent?.getThumbnail(
|
final src = mxContent?.getThumbnail(
|
||||||
Matrix.of(context).client,
|
Matrix.of(context).client,
|
||||||
width: bannerSize,
|
width: bannerSize,
|
||||||
height: bannerSize,
|
height: bannerSize,
|
||||||
|
@ -60,7 +60,7 @@ class ContentBanner extends StatelessWidget {
|
||||||
: Icon(defaultIcon, size: 300),
|
: Icon(defaultIcon, size: 300),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (this.onEdit != null)
|
if (onEdit != null)
|
||||||
Container(
|
Container(
|
||||||
margin: EdgeInsets.all(8),
|
margin: EdgeInsets.all(8),
|
||||||
alignment: Alignment.bottomRight,
|
alignment: Alignment.bottomRight,
|
||||||
|
|
|
@ -16,7 +16,7 @@ class RecordingDialog extends StatefulWidget {
|
||||||
|
|
||||||
class _RecordingDialogState extends State<RecordingDialog> {
|
class _RecordingDialogState extends State<RecordingDialog> {
|
||||||
FlutterSound flutterSound = FlutterSound();
|
FlutterSound flutterSound = FlutterSound();
|
||||||
String time = "00:00:00";
|
String time = '00:00:00';
|
||||||
|
|
||||||
StreamSubscription _recorderSubscription;
|
StreamSubscription _recorderSubscription;
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ class _RecordingDialogState extends State<RecordingDialog> {
|
||||||
codec: t_CODEC.CODEC_AAC,
|
codec: t_CODEC.CODEC_AAC,
|
||||||
);
|
);
|
||||||
_recorderSubscription = flutterSound.onRecorderStateChanged.listen((e) {
|
_recorderSubscription = flutterSound.onRecorderStateChanged.listen((e) {
|
||||||
DateTime date =
|
var date =
|
||||||
DateTime.fromMillisecondsSinceEpoch(e.currentPosition.toInt());
|
DateTime.fromMillisecondsSinceEpoch(e.currentPosition.toInt());
|
||||||
setState(() => time = DateFormat('mm:ss:SS', 'en_US').format(date));
|
setState(() => time = DateFormat('mm:ss:SS', 'en_US').format(date));
|
||||||
});
|
});
|
||||||
|
@ -67,7 +67,7 @@ class _RecordingDialogState extends State<RecordingDialog> {
|
||||||
SizedBox(width: 8),
|
SizedBox(width: 8),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Text(
|
child: Text(
|
||||||
"${L10n.of(context).recording}: $time",
|
'${L10n.of(context).recording}: $time',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 18,
|
fontSize: 18,
|
||||||
),
|
),
|
||||||
|
@ -95,7 +95,7 @@ class _RecordingDialogState extends State<RecordingDialog> {
|
||||||
),
|
),
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
await _recorderSubscription?.cancel();
|
await _recorderSubscription?.cancel();
|
||||||
final String result = await flutterSound.stopRecorder();
|
final result = await flutterSound.stopRecorder();
|
||||||
if (widget.onFinished != null) {
|
if (widget.onFinished != null) {
|
||||||
widget.onFinished(result);
|
widget.onFinished(result);
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,8 @@ class SimpleDialogs {
|
||||||
bool password = false,
|
bool password = false,
|
||||||
bool multiLine = false,
|
bool multiLine = false,
|
||||||
}) async {
|
}) async {
|
||||||
final TextEditingController controller = TextEditingController();
|
var textEditingController = TextEditingController();
|
||||||
|
final controller = textEditingController;
|
||||||
String input;
|
String input;
|
||||||
await showDialog(
|
await showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
|
@ -77,7 +78,7 @@ class SimpleDialogs {
|
||||||
String confirmText,
|
String confirmText,
|
||||||
String cancelText,
|
String cancelText,
|
||||||
}) async {
|
}) async {
|
||||||
bool confirmed = false;
|
var confirmed = false;
|
||||||
await showDialog(
|
await showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (c) => AlertDialog(
|
builder: (c) => AlertDialog(
|
||||||
|
@ -157,8 +158,8 @@ class SimpleDialogs {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
showLoadingDialog(BuildContext context) {
|
void showLoadingDialog(BuildContext context) async {
|
||||||
showDialog(
|
await showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
barrierDismissible: false,
|
barrierDismissible: false,
|
||||||
builder: (BuildContext context) => AlertDialog(
|
builder: (BuildContext context) => AlertDialog(
|
||||||
|
|
|
@ -67,7 +67,8 @@ class _EncryptionButtonState extends State<EncryptionButton> {
|
||||||
builder: (BuildContext context, snapshot) {
|
builder: (BuildContext context, snapshot) {
|
||||||
Color color;
|
Color color;
|
||||||
if (widget.room.encrypted && snapshot.hasData) {
|
if (widget.room.encrypted && snapshot.hasData) {
|
||||||
final List<DeviceKeys> deviceKeysList = snapshot.data;
|
var data = snapshot.data;
|
||||||
|
final deviceKeysList = data;
|
||||||
color = Colors.orange;
|
color = Colors.orange;
|
||||||
if (deviceKeysList.indexWhere((DeviceKeys deviceKeys) =>
|
if (deviceKeysList.indexWhere((DeviceKeys deviceKeys) =>
|
||||||
deviceKeys.verified == false &&
|
deviceKeys.verified == false &&
|
||||||
|
|
|
@ -15,7 +15,7 @@ class ImageBubble extends StatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _ImageBubbleState extends State<ImageBubble> {
|
class _ImageBubbleState extends State<ImageBubble> {
|
||||||
static Map<String, MatrixFile> _matrixFileMap = {};
|
static final Map<String, MatrixFile> _matrixFileMap = {};
|
||||||
MatrixFile get _file => _matrixFileMap[widget.event.eventId];
|
MatrixFile get _file => _matrixFileMap[widget.event.eventId];
|
||||||
set _file(MatrixFile file) {
|
set _file(MatrixFile file) {
|
||||||
_matrixFileMap[widget.event.eventId] = file;
|
_matrixFileMap[widget.event.eventId] = file;
|
||||||
|
@ -65,7 +65,7 @@ class _ImageBubbleState extends State<ImageBubble> {
|
||||||
}
|
}
|
||||||
_getFile().then((MatrixFile file) {
|
_getFile().then((MatrixFile file) {
|
||||||
setState(() => _file = file);
|
setState(() => _file = file);
|
||||||
}, onError: (error) {
|
}, onError: (error, stacktrace) {
|
||||||
setState(() => _error = error);
|
setState(() => _error = error);
|
||||||
});
|
});
|
||||||
return Center(
|
return Center(
|
||||||
|
|
|
@ -71,11 +71,11 @@ class ChatListItem extends StatelessWidget {
|
||||||
|
|
||||||
if (room.membership == Membership.join) {
|
if (room.membership == Membership.join) {
|
||||||
if (Matrix.of(context).shareContent != null) {
|
if (Matrix.of(context).shareContent != null) {
|
||||||
if (Matrix.of(context).shareContent["msgtype"] ==
|
if (Matrix.of(context).shareContent['msgtype'] ==
|
||||||
"chat.fluffy.shared_file") {
|
'chat.fluffy.shared_file') {
|
||||||
await SimpleDialogs(context).tryRequestWithErrorToast(
|
await SimpleDialogs(context).tryRequestWithErrorToast(
|
||||||
room.sendFileEvent(
|
room.sendFileEvent(
|
||||||
Matrix.of(context).shareContent["file"],
|
Matrix.of(context).shareContent['file'],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
@ -98,11 +98,11 @@ class ChatListItem extends StatelessWidget {
|
||||||
final success = await SimpleDialogs(context)
|
final success = await SimpleDialogs(context)
|
||||||
.tryRequestWithLoadingDialog(room.forget());
|
.tryRequestWithLoadingDialog(room.forget());
|
||||||
if (success != false) {
|
if (success != false) {
|
||||||
if (this.onForget != null) this.onForget();
|
if (onForget != null) onForget();
|
||||||
}
|
}
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
final bool confirmed = await SimpleDialogs(context).askConfirmation();
|
final confirmed = await SimpleDialogs(context).askConfirmation();
|
||||||
if (!confirmed) {
|
if (!confirmed) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -217,7 +217,7 @@ class ChatListItem extends StatelessWidget {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
: Text(" "),
|
: Text(' '),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
onTap: () => clickAction(context),
|
onTap: () => clickAction(context),
|
||||||
|
|
|
@ -37,23 +37,23 @@ class Message extends StatelessWidget {
|
||||||
return StateMessage(event);
|
return StateMessage(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
Client client = Matrix.of(context).client;
|
var client = Matrix.of(context).client;
|
||||||
final bool ownMessage = event.senderId == client.userID;
|
final ownMessage = event.senderId == client.userID;
|
||||||
Alignment alignment = ownMessage ? Alignment.topRight : Alignment.topLeft;
|
var alignment = ownMessage ? Alignment.topRight : Alignment.topLeft;
|
||||||
Color color = Theme.of(context).secondaryHeaderColor;
|
var color = Theme.of(context).secondaryHeaderColor;
|
||||||
final bool sameSender = nextEvent != null &&
|
final sameSender = nextEvent != null &&
|
||||||
[EventTypes.Message, EventTypes.Sticker].contains(nextEvent.type)
|
[EventTypes.Message, EventTypes.Sticker].contains(nextEvent.type)
|
||||||
? nextEvent.sender.id == event.sender.id
|
? nextEvent.sender.id == event.sender.id
|
||||||
: false;
|
: false;
|
||||||
BubbleNip nip = sameSender
|
var nip = sameSender
|
||||||
? BubbleNip.no
|
? BubbleNip.no
|
||||||
: ownMessage ? BubbleNip.rightBottom : BubbleNip.leftBottom;
|
: ownMessage ? BubbleNip.rightBottom : BubbleNip.leftBottom;
|
||||||
Color textColor = ownMessage
|
var textColor = ownMessage
|
||||||
? Colors.white
|
? Colors.white
|
||||||
: Theme.of(context).brightness == Brightness.dark
|
: Theme.of(context).brightness == Brightness.dark
|
||||||
? Colors.white
|
? Colors.white
|
||||||
: Colors.black;
|
: Colors.black;
|
||||||
MainAxisAlignment rowMainAxisAlignment =
|
var rowMainAxisAlignment =
|
||||||
ownMessage ? MainAxisAlignment.end : MainAxisAlignment.start;
|
ownMessage ? MainAxisAlignment.end : MainAxisAlignment.start;
|
||||||
|
|
||||||
if (event.showThumbnail) {
|
if (event.showThumbnail) {
|
||||||
|
@ -65,7 +65,7 @@ class Message extends StatelessWidget {
|
||||||
: Theme.of(context).primaryColor;
|
: Theme.of(context).primaryColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Widget> rowChildren = [
|
var rowChildren = <Widget>[
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Bubble(
|
child: Bubble(
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
|
@ -84,14 +84,14 @@ class Message extends StatelessWidget {
|
||||||
FutureBuilder<Event>(
|
FutureBuilder<Event>(
|
||||||
future: event.getReplyEvent(timeline),
|
future: event.getReplyEvent(timeline),
|
||||||
builder: (BuildContext context, snapshot) {
|
builder: (BuildContext context, snapshot) {
|
||||||
final Event replyEvent = snapshot.hasData
|
final replyEvent = snapshot.hasData
|
||||||
? snapshot.data
|
? snapshot.data
|
||||||
: Event(
|
: Event(
|
||||||
eventId: event.content['m.relates_to']
|
eventId: event.content['m.relates_to']
|
||||||
['m.in_reply_to']['event_id'],
|
['m.in_reply_to']['event_id'],
|
||||||
content: {"msgtype": "m.text", "body": "..."},
|
content: {'msgtype': 'm.text', 'body': '...'},
|
||||||
senderId: event.senderId,
|
senderId: event.senderId,
|
||||||
typeKey: "m.room.message",
|
typeKey: 'm.room.message',
|
||||||
room: event.room,
|
room: event.room,
|
||||||
roomId: event.roomId,
|
roomId: event.roomId,
|
||||||
status: 1,
|
status: 1,
|
||||||
|
@ -110,7 +110,7 @@ class Message extends StatelessWidget {
|
||||||
),
|
),
|
||||||
if (event.type == EventTypes.Encrypted &&
|
if (event.type == EventTypes.Encrypted &&
|
||||||
event.messageType == MessageTypes.BadEncrypted &&
|
event.messageType == MessageTypes.BadEncrypted &&
|
||||||
event.content["body"] == DecryptError.UNKNOWN_SESSION)
|
event.content['body'] == DecryptError.UNKNOWN_SESSION)
|
||||||
RaisedButton(
|
RaisedButton(
|
||||||
color: color.withAlpha(100),
|
color: color.withAlpha(100),
|
||||||
child: Text(
|
child: Text(
|
||||||
|
@ -146,7 +146,7 @@ class Message extends StatelessWidget {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
final Widget avatarOrSizedBox = sameSender
|
final avatarOrSizedBox = sameSender
|
||||||
? SizedBox(width: Avatar.defaultSize)
|
? SizedBox(width: Avatar.defaultSize)
|
||||||
: Avatar(
|
: Avatar(
|
||||||
event.sender.avatarUrl,
|
event.sender.avatarUrl,
|
||||||
|
@ -193,8 +193,8 @@ class _MetaRow extends StatelessWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final String displayname = event.sender.calcDisplayname();
|
final displayname = event.sender.calcDisplayname();
|
||||||
final bool showDisplayname =
|
final showDisplayname =
|
||||||
!ownMessage && event.senderId != event.room.directChatMatrixID;
|
!ownMessage && event.senderId != event.room.directChatMatrixID;
|
||||||
return Row(
|
return Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
|
|
@ -13,44 +13,44 @@ class ParticipantListItem extends StatelessWidget {
|
||||||
|
|
||||||
const ParticipantListItem(this.user);
|
const ParticipantListItem(this.user);
|
||||||
|
|
||||||
participantAction(BuildContext context, String action) async {
|
void participantAction(BuildContext context, String action) async {
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case "ban":
|
case 'ban':
|
||||||
if (await SimpleDialogs(context).askConfirmation()) {
|
if (await SimpleDialogs(context).askConfirmation()) {
|
||||||
await SimpleDialogs(context).tryRequestWithLoadingDialog(user.ban());
|
await SimpleDialogs(context).tryRequestWithLoadingDialog(user.ban());
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "unban":
|
case 'unban':
|
||||||
if (await SimpleDialogs(context).askConfirmation()) {
|
if (await SimpleDialogs(context).askConfirmation()) {
|
||||||
await SimpleDialogs(context)
|
await SimpleDialogs(context)
|
||||||
.tryRequestWithLoadingDialog(user.unban());
|
.tryRequestWithLoadingDialog(user.unban());
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "kick":
|
case 'kick':
|
||||||
if (await SimpleDialogs(context).askConfirmation()) {
|
if (await SimpleDialogs(context).askConfirmation()) {
|
||||||
await SimpleDialogs(context).tryRequestWithLoadingDialog(user.kick());
|
await SimpleDialogs(context).tryRequestWithLoadingDialog(user.kick());
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "admin":
|
case 'admin':
|
||||||
if (await SimpleDialogs(context).askConfirmation()) {
|
if (await SimpleDialogs(context).askConfirmation()) {
|
||||||
await SimpleDialogs(context)
|
await SimpleDialogs(context)
|
||||||
.tryRequestWithLoadingDialog(user.setPower(100));
|
.tryRequestWithLoadingDialog(user.setPower(100));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "moderator":
|
case 'moderator':
|
||||||
if (await SimpleDialogs(context).askConfirmation()) {
|
if (await SimpleDialogs(context).askConfirmation()) {
|
||||||
await SimpleDialogs(context)
|
await SimpleDialogs(context)
|
||||||
.tryRequestWithLoadingDialog(user.setPower(50));
|
.tryRequestWithLoadingDialog(user.setPower(50));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "user":
|
case 'user':
|
||||||
if (await SimpleDialogs(context).askConfirmation()) {
|
if (await SimpleDialogs(context).askConfirmation()) {
|
||||||
await SimpleDialogs(context)
|
await SimpleDialogs(context)
|
||||||
.tryRequestWithLoadingDialog(user.setPower(0));
|
.tryRequestWithLoadingDialog(user.setPower(0));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "message":
|
case 'message':
|
||||||
final String roomId = await user.startDirectChat();
|
final roomId = await user.startDirectChat();
|
||||||
await Navigator.of(context).pushAndRemoveUntil(
|
await Navigator.of(context).pushAndRemoveUntil(
|
||||||
AppRoute.defaultRoute(
|
AppRoute.defaultRoute(
|
||||||
context,
|
context,
|
||||||
|
@ -63,21 +63,21 @@ class ParticipantListItem extends StatelessWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
Map<Membership, String> membershipBatch = {
|
var membershipBatch = <Membership, String>{
|
||||||
Membership.join: "",
|
Membership.join: '',
|
||||||
Membership.ban: L10n.of(context).banned,
|
Membership.ban: L10n.of(context).banned,
|
||||||
Membership.invite: L10n.of(context).invited,
|
Membership.invite: L10n.of(context).invited,
|
||||||
Membership.leave: L10n.of(context).leftTheChat,
|
Membership.leave: L10n.of(context).leftTheChat,
|
||||||
};
|
};
|
||||||
final String permissionBatch = user.powerLevel == 100
|
final permissionBatch = user.powerLevel == 100
|
||||||
? L10n.of(context).admin
|
? L10n.of(context).admin
|
||||||
: user.powerLevel >= 50 ? L10n.of(context).moderator : "";
|
: user.powerLevel >= 50 ? L10n.of(context).moderator : '';
|
||||||
List<PopupMenuEntry<String>> items = <PopupMenuEntry<String>>[];
|
var items = <PopupMenuEntry<String>>[];
|
||||||
|
|
||||||
if (user.id != Matrix.of(context).client.userID) {
|
if (user.id != Matrix.of(context).client.userID) {
|
||||||
items.add(
|
items.add(
|
||||||
PopupMenuItem(
|
PopupMenuItem(
|
||||||
child: Text(L10n.of(context).sendAMessage), value: "message"),
|
child: Text(L10n.of(context).sendAMessage), value: 'message'),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (user.canChangePowerLevel &&
|
if (user.canChangePowerLevel &&
|
||||||
|
@ -85,7 +85,7 @@ class ParticipantListItem extends StatelessWidget {
|
||||||
user.powerLevel != 100) {
|
user.powerLevel != 100) {
|
||||||
items.add(
|
items.add(
|
||||||
PopupMenuItem(
|
PopupMenuItem(
|
||||||
child: Text(L10n.of(context).makeAnAdmin), value: "admin"),
|
child: Text(L10n.of(context).makeAnAdmin), value: 'admin'),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (user.canChangePowerLevel &&
|
if (user.canChangePowerLevel &&
|
||||||
|
@ -93,29 +93,29 @@ class ParticipantListItem extends StatelessWidget {
|
||||||
user.powerLevel != 50) {
|
user.powerLevel != 50) {
|
||||||
items.add(
|
items.add(
|
||||||
PopupMenuItem(
|
PopupMenuItem(
|
||||||
child: Text(L10n.of(context).makeAModerator), value: "moderator"),
|
child: Text(L10n.of(context).makeAModerator), value: 'moderator'),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (user.canChangePowerLevel && user.powerLevel != 0) {
|
if (user.canChangePowerLevel && user.powerLevel != 0) {
|
||||||
items.add(
|
items.add(
|
||||||
PopupMenuItem(
|
PopupMenuItem(
|
||||||
child: Text(L10n.of(context).revokeAllPermissions), value: "user"),
|
child: Text(L10n.of(context).revokeAllPermissions), value: 'user'),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (user.canKick) {
|
if (user.canKick) {
|
||||||
items.add(
|
items.add(
|
||||||
PopupMenuItem(
|
PopupMenuItem(
|
||||||
child: Text(L10n.of(context).kickFromChat), value: "kick"),
|
child: Text(L10n.of(context).kickFromChat), value: 'kick'),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (user.canBan && user.membership != Membership.ban) {
|
if (user.canBan && user.membership != Membership.ban) {
|
||||||
items.add(
|
items.add(
|
||||||
PopupMenuItem(child: Text(L10n.of(context).banFromChat), value: "ban"),
|
PopupMenuItem(child: Text(L10n.of(context).banFromChat), value: 'ban'),
|
||||||
);
|
);
|
||||||
} else if (user.canBan && user.membership == Membership.ban) {
|
} else if (user.canBan && user.membership == Membership.ban) {
|
||||||
items.add(
|
items.add(
|
||||||
PopupMenuItem(
|
PopupMenuItem(
|
||||||
child: Text(L10n.of(context).removeExile), value: "unban"),
|
child: Text(L10n.of(context).removeExile), value: 'unban'),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return PopupMenuButton(
|
return PopupMenuButton(
|
||||||
|
|
|
@ -13,7 +13,7 @@ class PresenceListItem extends StatelessWidget {
|
||||||
|
|
||||||
const PresenceListItem(this.presence);
|
const PresenceListItem(this.presence);
|
||||||
|
|
||||||
static Map<String, Profile> _presences = {};
|
static final Map<String, Profile> _presences = {};
|
||||||
|
|
||||||
Future<Profile> _requestProfile(BuildContext context) async {
|
Future<Profile> _requestProfile(BuildContext context) async {
|
||||||
_presences[presence.sender] ??=
|
_presences[presence.sender] ??=
|
||||||
|
@ -28,7 +28,7 @@ class PresenceListItem extends StatelessWidget {
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
if (!snapshot.hasData) return Container();
|
if (!snapshot.hasData) return Container();
|
||||||
Uri avatarUrl;
|
Uri avatarUrl;
|
||||||
String displayname = presence.sender.localpart;
|
var displayname = presence.sender.localpart;
|
||||||
if (snapshot.hasData) {
|
if (snapshot.hasData) {
|
||||||
avatarUrl = snapshot.data.avatarUrl;
|
avatarUrl = snapshot.data.avatarUrl;
|
||||||
displayname = snapshot.data.displayname;
|
displayname = snapshot.data.displayname;
|
||||||
|
@ -64,7 +64,7 @@ class PresenceListItem extends StatelessWidget {
|
||||||
FlatButton(
|
FlatButton(
|
||||||
child: Text(L10n.of(context).sendAMessage),
|
child: Text(L10n.of(context).sendAMessage),
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
final String roomId = await User(
|
final roomId = await User(
|
||||||
presence.sender,
|
presence.sender,
|
||||||
room: Room(id: '', client: Matrix.of(context).client),
|
room: Room(id: '', client: Matrix.of(context).client),
|
||||||
).startDirectChat();
|
).startDirectChat();
|
||||||
|
|
|
@ -27,7 +27,7 @@ class PublicRoomListItem extends StatelessWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final bool hasTopic =
|
final hasTopic =
|
||||||
publicRoomEntry.topic != null && publicRoomEntry.topic.isNotEmpty;
|
publicRoomEntry.topic != null && publicRoomEntry.topic.isNotEmpty;
|
||||||
return ListTile(
|
return ListTile(
|
||||||
leading: Avatar(
|
leading: Avatar(
|
||||||
|
@ -36,13 +36,13 @@ class PublicRoomListItem extends StatelessWidget {
|
||||||
: Uri.parse(publicRoomEntry.avatarUrl),
|
: Uri.parse(publicRoomEntry.avatarUrl),
|
||||||
publicRoomEntry.name),
|
publicRoomEntry.name),
|
||||||
title: Text(hasTopic
|
title: Text(hasTopic
|
||||||
? "${publicRoomEntry.name} (${publicRoomEntry.numJoinedMembers})"
|
? '${publicRoomEntry.name} (${publicRoomEntry.numJoinedMembers})'
|
||||||
: publicRoomEntry.name),
|
: publicRoomEntry.name),
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
hasTopic
|
hasTopic
|
||||||
? publicRoomEntry.topic
|
? publicRoomEntry.topic
|
||||||
: L10n.of(context).countParticipants(
|
: L10n.of(context).countParticipants(
|
||||||
publicRoomEntry.numJoinedMembers?.toString() ?? "0"),
|
publicRoomEntry.numJoinedMembers?.toString() ?? '0'),
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
),
|
),
|
||||||
onTap: () => joinAction(context),
|
onTap: () => joinAction(context),
|
||||||
|
|
|
@ -24,14 +24,17 @@ class Matrix extends StatefulWidget {
|
||||||
|
|
||||||
final Client client;
|
final Client client;
|
||||||
|
|
||||||
Matrix({this.child, this.clientName, this.client, Key key}) : super(key: key);
|
final Store store;
|
||||||
|
|
||||||
|
Matrix({this.child, this.clientName, this.client, this.store, Key key})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
MatrixState createState() => MatrixState();
|
MatrixState createState() => MatrixState();
|
||||||
|
|
||||||
/// Returns the (nearest) Client instance of your application.
|
/// Returns the (nearest) Client instance of your application.
|
||||||
static MatrixState of(BuildContext context) {
|
static MatrixState of(BuildContext context) {
|
||||||
MatrixState newState =
|
var newState =
|
||||||
(context.dependOnInheritedWidgetOfExactType<_InheritedMatrix>()).data;
|
(context.dependOnInheritedWidgetOfExactType<_InheritedMatrix>()).data;
|
||||||
newState.context = FirebaseController.context = context;
|
newState.context = FirebaseController.context = context;
|
||||||
return newState;
|
return newState;
|
||||||
|
@ -40,6 +43,8 @@ class Matrix extends StatefulWidget {
|
||||||
|
|
||||||
class MatrixState extends State<Matrix> {
|
class MatrixState extends State<Matrix> {
|
||||||
Client client;
|
Client client;
|
||||||
|
Store store;
|
||||||
|
@override
|
||||||
BuildContext context;
|
BuildContext context;
|
||||||
|
|
||||||
Map<String, dynamic> get shareContent => _shareContent;
|
Map<String, dynamic> get shareContent => _shareContent;
|
||||||
|
@ -62,16 +67,15 @@ class MatrixState extends State<Matrix> {
|
||||||
void clean() async {
|
void clean() async {
|
||||||
if (!kIsWeb) return;
|
if (!kIsWeb) return;
|
||||||
|
|
||||||
final LocalStorage storage = LocalStorage('LocalStorage');
|
final storage = LocalStorage('LocalStorage');
|
||||||
await storage.ready;
|
await storage.ready;
|
||||||
await storage.deleteItem(widget.clientName);
|
await storage.deleteItem(widget.clientName);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _initWithStore() async {
|
void _initWithStore() async {
|
||||||
Future<LoginState> initLoginState = client.onLoginStateChanged.stream.first;
|
var initLoginState = client.onLoginStateChanged.stream.first;
|
||||||
client.storeAPI = kIsWeb ? Store(client) : ExtendedStore(client);
|
client.database = await getDatabase(client, store);
|
||||||
debugPrint(
|
client.connect();
|
||||||
"[Store] Store is extended: ${client.storeAPI.extended.toString()}");
|
|
||||||
if (await initLoginState == LoginState.logged && !kIsWeb) {
|
if (await initLoginState == LoginState.logged && !kIsWeb) {
|
||||||
await FirebaseController.setupFirebase(
|
await FirebaseController.setupFirebase(
|
||||||
client,
|
client,
|
||||||
|
@ -81,14 +85,14 @@ class MatrixState extends State<Matrix> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, dynamic> getAuthByPassword(String password, String session) => {
|
Map<String, dynamic> getAuthByPassword(String password, String session) => {
|
||||||
"type": "m.login.password",
|
'type': 'm.login.password',
|
||||||
"identifier": {
|
'identifier': {
|
||||||
"type": "m.id.user",
|
'type': 'm.id.user',
|
||||||
"user": client.userID,
|
'user': client.userID,
|
||||||
},
|
},
|
||||||
"user": client.userID,
|
'user': client.userID,
|
||||||
"password": password,
|
'password': password,
|
||||||
"session": session,
|
'session': session,
|
||||||
};
|
};
|
||||||
|
|
||||||
StreamSubscription onRoomKeyRequestSub;
|
StreamSubscription onRoomKeyRequestSub;
|
||||||
|
@ -151,8 +155,9 @@ class MatrixState extends State<Matrix> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
|
store = widget.store ?? Store();
|
||||||
if (widget.client == null) {
|
if (widget.client == null) {
|
||||||
debugPrint("[Matrix] Init matrix client");
|
debugPrint('[Matrix] Init matrix client');
|
||||||
client = Client(widget.clientName, debug: false);
|
client = Client(widget.clientName, debug: false);
|
||||||
onJitsiCallSub ??= client.onEvent.stream
|
onJitsiCallSub ??= client.onEvent.stream
|
||||||
.where((e) =>
|
.where((e) =>
|
||||||
|
@ -163,12 +168,12 @@ class MatrixState extends State<Matrix> {
|
||||||
.listen(onJitsiCall);
|
.listen(onJitsiCall);
|
||||||
onRoomKeyRequestSub ??=
|
onRoomKeyRequestSub ??=
|
||||||
client.onRoomKeyRequest.stream.listen((RoomKeyRequest request) async {
|
client.onRoomKeyRequest.stream.listen((RoomKeyRequest request) async {
|
||||||
final Room room = request.room;
|
final room = request.room;
|
||||||
final User sender = room.getUserByMXIDSync(request.sender);
|
final sender = room.getUserByMXIDSync(request.sender);
|
||||||
if (await SimpleDialogs(context).askConfirmation(
|
if (await SimpleDialogs(context).askConfirmation(
|
||||||
titleText: L10n.of(context).requestToReadOlderMessages,
|
titleText: L10n.of(context).requestToReadOlderMessages,
|
||||||
contentText:
|
contentText:
|
||||||
"${sender.id}\n\n${L10n.of(context).device}:\n${request.requestingDevice.deviceId}\n\n${L10n.of(context).identity}:\n${request.requestingDevice.curve25519Key.beautified}",
|
'${sender.id}\n\n${L10n.of(context).device}:\n${request.requestingDevice.deviceId}\n\n${L10n.of(context).identity}:\n${request.requestingDevice.curve25519Key.beautified}',
|
||||||
confirmText: L10n.of(context).verify,
|
confirmText: L10n.of(context).verify,
|
||||||
cancelText: L10n.of(context).deny,
|
cancelText: L10n.of(context).deny,
|
||||||
)) {
|
)) {
|
||||||
|
@ -178,20 +183,21 @@ class MatrixState extends State<Matrix> {
|
||||||
_initWithStore();
|
_initWithStore();
|
||||||
} else {
|
} else {
|
||||||
client = widget.client;
|
client = widget.client;
|
||||||
|
client.connect();
|
||||||
}
|
}
|
||||||
if (client.storeAPI != null) {
|
if (store != null) {
|
||||||
client.storeAPI
|
store
|
||||||
.getItem("chat.fluffy.jitsi_instance")
|
.getItem('chat.fluffy.jitsi_instance')
|
||||||
.then((final instance) => jitsiInstance = instance ?? jitsiInstance);
|
.then((final instance) => jitsiInstance = instance ?? jitsiInstance);
|
||||||
client.storeAPI.getItem("chat.fluffy.wallpaper").then((final path) async {
|
store.getItem('chat.fluffy.wallpaper').then((final path) async {
|
||||||
if (path == null) return;
|
if (path == null) return;
|
||||||
final file = File(path);
|
final file = File(path);
|
||||||
if (await file.exists()) {
|
if (await file.exists()) {
|
||||||
wallpaper = file;
|
wallpaper = file;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
client.storeAPI.getItem("chat.fluffy.renderHtml").then((final render) async {
|
store.getItem('chat.fluffy.renderHtml').then((final render) async {
|
||||||
renderHtml = render == "1";
|
renderHtml = render == '1';
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
super.initState();
|
super.initState();
|
||||||
|
@ -221,12 +227,11 @@ class _InheritedMatrix extends InheritedWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool updateShouldNotify(_InheritedMatrix old) {
|
bool updateShouldNotify(_InheritedMatrix old) {
|
||||||
bool update = old.data.client.accessToken != this.data.client.accessToken ||
|
var update = old.data.client.accessToken != data.client.accessToken ||
|
||||||
old.data.client.userID != this.data.client.userID ||
|
old.data.client.userID != data.client.userID ||
|
||||||
old.data.client.matrixVersions != this.data.client.matrixVersions ||
|
old.data.client.deviceID != data.client.deviceID ||
|
||||||
old.data.client.deviceID != this.data.client.deviceID ||
|
old.data.client.deviceName != data.client.deviceName ||
|
||||||
old.data.client.deviceName != this.data.client.deviceName ||
|
old.data.client.homeserver != data.client.homeserver;
|
||||||
old.data.client.homeserver != this.data.client.homeserver;
|
|
||||||
return update;
|
return update;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,14 +40,13 @@ class MessageContent extends StatelessWidget {
|
||||||
case MessageTypes.Text:
|
case MessageTypes.Text:
|
||||||
case MessageTypes.Notice:
|
case MessageTypes.Notice:
|
||||||
case MessageTypes.Emote:
|
case MessageTypes.Emote:
|
||||||
if (
|
if (Matrix.of(context).renderHtml &&
|
||||||
Matrix.of(context).renderHtml && !event.redacted &&
|
!event.redacted &&
|
||||||
event.content['format'] == 'org.matrix.custom.html' &&
|
event.content['format'] == 'org.matrix.custom.html' &&
|
||||||
event.content['formatted_body'] is String
|
event.content['formatted_body'] is String) {
|
||||||
) {
|
|
||||||
String html = event.content['formatted_body'];
|
String html = event.content['formatted_body'];
|
||||||
if (event.messageType == MessageTypes.Emote) {
|
if (event.messageType == MessageTypes.Emote) {
|
||||||
html = "* $html";
|
html = '* $html';
|
||||||
}
|
}
|
||||||
return HtmlMessage(
|
return HtmlMessage(
|
||||||
html: html,
|
html: html,
|
||||||
|
|
|
@ -36,9 +36,9 @@ class MessageDownloadContent extends StatelessWidget {
|
||||||
matrixFile.open();
|
matrixFile.open();
|
||||||
}),
|
}),
|
||||||
Text(
|
Text(
|
||||||
"- " +
|
'- ' +
|
||||||
(event.content.containsKey("filename")
|
(event.content.containsKey('filename')
|
||||||
? event.content["filename"]
|
? event.content['filename']
|
||||||
: event.body),
|
: event.body),
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: textColor,
|
color: textColor,
|
||||||
|
@ -47,7 +47,7 @@ class MessageDownloadContent extends StatelessWidget {
|
||||||
),
|
),
|
||||||
if (event.sizeString != null)
|
if (event.sizeString != null)
|
||||||
Text(
|
Text(
|
||||||
"- " + event.sizeString,
|
'- ' + event.sizeString,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: textColor,
|
color: textColor,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
|
|
|
@ -15,15 +15,17 @@ class ReplyContent extends StatelessWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
Widget replyBody;
|
Widget replyBody;
|
||||||
if (
|
if (replyEvent != null &&
|
||||||
replyEvent != null && Matrix.of(context).renderHtml &&
|
Matrix.of(context).renderHtml &&
|
||||||
[EventTypes.Message, EventTypes.Encrypted].contains(replyEvent.type) &&
|
[EventTypes.Message, EventTypes.Encrypted].contains(replyEvent.type) &&
|
||||||
[MessageTypes.Text, MessageTypes.Notice, MessageTypes.Emote].contains(replyEvent.messageType) &&
|
[MessageTypes.Text, MessageTypes.Notice, MessageTypes.Emote]
|
||||||
!replyEvent.redacted && replyEvent.content['format'] == 'org.matrix.custom.html' && replyEvent.content['formatted_body'] is String
|
.contains(replyEvent.messageType) &&
|
||||||
) {
|
!replyEvent.redacted &&
|
||||||
|
replyEvent.content['format'] == 'org.matrix.custom.html' &&
|
||||||
|
replyEvent.content['formatted_body'] is String) {
|
||||||
String html = replyEvent.content['formatted_body'];
|
String html = replyEvent.content['formatted_body'];
|
||||||
if (replyEvent.messageType == MessageTypes.Emote) {
|
if (replyEvent.messageType == MessageTypes.Emote) {
|
||||||
html = "* $html";
|
html = '* $html';
|
||||||
}
|
}
|
||||||
replyBody = HtmlMessage(
|
replyBody = HtmlMessage(
|
||||||
html: html,
|
html: html,
|
||||||
|
@ -39,7 +41,7 @@ class ReplyContent extends StatelessWidget {
|
||||||
withSenderNamePrefix: false,
|
withSenderNamePrefix: false,
|
||||||
hideReply: true,
|
hideReply: true,
|
||||||
) ??
|
) ??
|
||||||
"",
|
'',
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
|
@ -62,7 +64,7 @@ class ReplyContent extends StatelessWidget {
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text(
|
Text(
|
||||||
(replyEvent?.sender?.calcDisplayname() ?? "") + ":",
|
(replyEvent?.sender?.calcDisplayname() ?? '') + ':',
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
|
|
|
@ -15,9 +15,8 @@ class ThemesSettingsState extends State<ThemesSettings> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final MatrixState matrix = Matrix.of(context);
|
final matrix = Matrix.of(context);
|
||||||
final ThemeSwitcherWidgetState themeEngine =
|
final themeEngine = ThemeSwitcherWidget.of(context);
|
||||||
ThemeSwitcherWidget.of(context);
|
|
||||||
_selectedTheme = themeEngine.selectedTheme;
|
_selectedTheme = themeEngine.selectedTheme;
|
||||||
_amoledEnabled = themeEngine.amoledEnabled;
|
_amoledEnabled = themeEngine.amoledEnabled;
|
||||||
|
|
||||||
|
|
|
@ -147,7 +147,7 @@ class ThemeSwitcher extends InheritedWidget {
|
||||||
class ThemeSwitcherWidget extends StatefulWidget {
|
class ThemeSwitcherWidget extends StatefulWidget {
|
||||||
final Widget child;
|
final Widget child;
|
||||||
|
|
||||||
ThemeSwitcherWidget({Key key, this.child})
|
ThemeSwitcherWidget({Key key, @required this.child})
|
||||||
: assert(child != null),
|
: assert(child != null),
|
||||||
super(key: key);
|
super(key: key);
|
||||||
|
|
||||||
|
@ -156,7 +156,7 @@ class ThemeSwitcherWidget extends StatefulWidget {
|
||||||
|
|
||||||
/// Returns the (nearest) Client instance of your application.
|
/// Returns the (nearest) Client instance of your application.
|
||||||
static ThemeSwitcherWidgetState of(BuildContext context) {
|
static ThemeSwitcherWidgetState of(BuildContext context) {
|
||||||
ThemeSwitcherWidgetState newState =
|
var newState =
|
||||||
(context.dependOnInheritedWidgetOfExactType<ThemeSwitcher>()).data;
|
(context.dependOnInheritedWidgetOfExactType<ThemeSwitcher>()).data;
|
||||||
newState.context = context;
|
newState.context = context;
|
||||||
return newState;
|
return newState;
|
||||||
|
@ -167,15 +167,15 @@ class ThemeSwitcherWidgetState extends State<ThemeSwitcherWidget> {
|
||||||
ThemeData themeData;
|
ThemeData themeData;
|
||||||
Themes selectedTheme;
|
Themes selectedTheme;
|
||||||
bool amoledEnabled;
|
bool amoledEnabled;
|
||||||
|
@override
|
||||||
BuildContext context;
|
BuildContext context;
|
||||||
|
|
||||||
Future loadSelection(MatrixState matrix) async {
|
Future loadSelection(MatrixState matrix) async {
|
||||||
String item = await matrix.client.storeAPI.getItem("theme") ?? "light";
|
String item = await matrix.store.getItem('theme') ?? 'light';
|
||||||
selectedTheme =
|
selectedTheme =
|
||||||
Themes.values.firstWhere((e) => e.toString() == 'Themes.' + item);
|
Themes.values.firstWhere((e) => e.toString() == 'Themes.' + item);
|
||||||
|
|
||||||
amoledEnabled =
|
amoledEnabled = (await matrix.store.getItem('amoled_enabled') ?? 'false')
|
||||||
(await matrix.client.storeAPI.getItem("amoled_enabled") ?? "false")
|
|
||||||
.toLowerCase() ==
|
.toLowerCase() ==
|
||||||
'true';
|
'true';
|
||||||
|
|
||||||
|
@ -199,7 +199,7 @@ class ThemeSwitcherWidgetState extends State<ThemeSwitcherWidget> {
|
||||||
break;
|
break;
|
||||||
case Themes.system:
|
case Themes.system:
|
||||||
// This needs to be a low level call as we don't have a MaterialApp yet
|
// This needs to be a low level call as we don't have a MaterialApp yet
|
||||||
Brightness brightness =
|
var brightness =
|
||||||
MediaQueryData.fromWindow(WidgetsBinding.instance.window)
|
MediaQueryData.fromWindow(WidgetsBinding.instance.window)
|
||||||
.platformBrightness;
|
.platformBrightness;
|
||||||
if (brightness == Brightness.dark) {
|
if (brightness == Brightness.dark) {
|
||||||
|
@ -224,16 +224,15 @@ class ThemeSwitcherWidgetState extends State<ThemeSwitcherWidget> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future saveThemeValue(MatrixState matrix, Themes value) async {
|
Future saveThemeValue(MatrixState matrix, Themes value) async {
|
||||||
await matrix.client.storeAPI
|
await matrix.store.setItem('theme', value.toString().split('.').last);
|
||||||
.setItem("theme", value.toString().split('.').last);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future saveAmoledEnabledValue(MatrixState matrix, bool value) async {
|
Future saveAmoledEnabledValue(MatrixState matrix, bool value) async {
|
||||||
await matrix.client.storeAPI.setItem("amoled_enabled", value.toString());
|
await matrix.store.setItem('amoled_enabled', value.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
void setup() async {
|
void setup() async {
|
||||||
final MatrixState matrix = Matrix.of(context);
|
final matrix = Matrix.of(context);
|
||||||
await loadSelection(matrix);
|
await loadSelection(matrix);
|
||||||
|
|
||||||
if (selectedTheme == null) {
|
if (selectedTheme == null) {
|
||||||
|
@ -271,8 +270,7 @@ class ThemeSwitcherWidgetState extends State<ThemeSwitcherWidget> {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
if (themeData == null) {
|
if (themeData == null) {
|
||||||
// This needs to be a low level call as we don't have a MaterialApp yet
|
// This needs to be a low level call as we don't have a MaterialApp yet
|
||||||
Brightness brightness =
|
var brightness = MediaQueryData.fromWindow(WidgetsBinding.instance.window)
|
||||||
MediaQueryData.fromWindow(WidgetsBinding.instance.window)
|
|
||||||
.platformBrightness;
|
.platformBrightness;
|
||||||
if (brightness == Brightness.dark) {
|
if (brightness == Brightness.dark) {
|
||||||
themeData = darkTheme;
|
themeData = darkTheme;
|
||||||
|
|
|
@ -21,11 +21,11 @@ void main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
class App extends StatelessWidget {
|
class App extends StatelessWidget {
|
||||||
final String platform = kIsWeb ? "Web" : Platform.operatingSystem;
|
final String platform = kIsWeb ? 'Web' : Platform.operatingSystem;
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Matrix(
|
return Matrix(
|
||||||
clientName: "FluffyChat $platform",
|
clientName: 'FluffyChat $platform',
|
||||||
child: Builder(
|
child: Builder(
|
||||||
builder: (BuildContext context) => ThemeSwitcherWidget(
|
builder: (BuildContext context) => ThemeSwitcherWidget(
|
||||||
child: Builder(
|
child: Builder(
|
||||||
|
@ -47,7 +47,7 @@ class App extends StatelessWidget {
|
||||||
const Locale('pl'), // Polish
|
const Locale('pl'), // Polish
|
||||||
],
|
],
|
||||||
locale: kIsWeb
|
locale: kIsWeb
|
||||||
? Locale(html.window.navigator.language.split("-").first)
|
? Locale(html.window.navigator.language.split('-').first)
|
||||||
: null,
|
: null,
|
||||||
home: FutureBuilder<LoginState>(
|
home: FutureBuilder<LoginState>(
|
||||||
future:
|
future:
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
extension BeautifyStringExtension on String {
|
extension BeautifyStringExtension on String {
|
||||||
String get beautified {
|
String get beautified {
|
||||||
String beautifiedStr = "";
|
var beautifiedStr = '';
|
||||||
for (int i = 0; i < this.length; i++) {
|
for (var i = 0; i < length; i++) {
|
||||||
beautifiedStr += this.substring(i, i + 1);
|
beautifiedStr += substring(i, i + 1);
|
||||||
if (i % 4 == 3) {
|
if (i % 4 == 3) {
|
||||||
beautifiedStr += " ";
|
beautifiedStr += ' ';
|
||||||
}
|
}
|
||||||
if (i % 16 == 15) {
|
if (i % 16 == 15) {
|
||||||
beautifiedStr += "\n";
|
beautifiedStr += '\n';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return beautifiedStr;
|
return beautifiedStr;
|
||||||
|
|
12
lib/utils/database/mobile.dart
Normal file
12
lib/utils/database/mobile.dart
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
import 'package:famedlysdk/famedlysdk.dart';
|
||||||
|
import 'package:encrypted_moor/encrypted_moor.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
Database constructDb({bool logStatements = false, String filename = 'database.sqlite', String password = ''}) {
|
||||||
|
debugPrint('[Moor] using encrypted moor');
|
||||||
|
return Database(EncryptedExecutor(path: filename, password: password, logStatements: logStatements));
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<String> getLocalstorage(String key) async {
|
||||||
|
return null;
|
||||||
|
}
|
3
lib/utils/database/shared.dart
Normal file
3
lib/utils/database/shared.dart
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
export 'unsupported.dart'
|
||||||
|
if (dart.library.html) 'web.dart'
|
||||||
|
if (dart.library.io) 'mobile.dart';
|
9
lib/utils/database/unsupported.dart
Normal file
9
lib/utils/database/unsupported.dart
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
import 'package:famedlysdk/famedlysdk.dart';
|
||||||
|
|
||||||
|
Database constructDb({bool logStatements = false, String filename = 'database.sqlite', String password = ''}) {
|
||||||
|
throw 'Platform not supported';
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<String> getLocalstorage(String key) async {
|
||||||
|
return null;
|
||||||
|
}
|
13
lib/utils/database/web.dart
Normal file
13
lib/utils/database/web.dart
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
import 'package:famedlysdk/famedlysdk.dart';
|
||||||
|
import 'package:moor/moor_web.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'dart:html';
|
||||||
|
|
||||||
|
Database constructDb({bool logStatements = false, String filename = 'database.sqlite', String password = ''}) {
|
||||||
|
debugPrint('[Moor] Using moor web');
|
||||||
|
return Database(WebDatabase.withStorage(MoorWebStorage.indexedDbIfSupported(filename), logStatements: logStatements));
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<String> getLocalstorage(String key) async {
|
||||||
|
return await window.localStorage[key];
|
||||||
|
}
|
|
@ -3,20 +3,20 @@ import 'package:flutter/material.dart';
|
||||||
|
|
||||||
/// Provides extra functionality for formatting the time.
|
/// Provides extra functionality for formatting the time.
|
||||||
extension DateTimeExtension on DateTime {
|
extension DateTimeExtension on DateTime {
|
||||||
operator <(DateTime other) {
|
bool operator <(DateTime other) {
|
||||||
return this.millisecondsSinceEpoch < other.millisecondsSinceEpoch;
|
return millisecondsSinceEpoch < other.millisecondsSinceEpoch;
|
||||||
}
|
}
|
||||||
|
|
||||||
operator >(DateTime other) {
|
bool operator >(DateTime other) {
|
||||||
return this.millisecondsSinceEpoch > other.millisecondsSinceEpoch;
|
return millisecondsSinceEpoch > other.millisecondsSinceEpoch;
|
||||||
}
|
}
|
||||||
|
|
||||||
operator >=(DateTime other) {
|
bool operator >=(DateTime other) {
|
||||||
return this.millisecondsSinceEpoch >= other.millisecondsSinceEpoch;
|
return millisecondsSinceEpoch >= other.millisecondsSinceEpoch;
|
||||||
}
|
}
|
||||||
|
|
||||||
operator <=(DateTime other) {
|
bool operator <=(DateTime other) {
|
||||||
return this.millisecondsSinceEpoch <= other.millisecondsSinceEpoch;
|
return millisecondsSinceEpoch <= other.millisecondsSinceEpoch;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Two message events can belong to the same environment. That means that they
|
/// Two message events can belong to the same environment. That means that they
|
||||||
|
@ -34,28 +34,28 @@ extension DateTimeExtension on DateTime {
|
||||||
/// Returns a simple time String.
|
/// Returns a simple time String.
|
||||||
/// TODO: Add localization
|
/// TODO: Add localization
|
||||||
String localizedTimeOfDay(BuildContext context) {
|
String localizedTimeOfDay(BuildContext context) {
|
||||||
return L10n.of(context).timeOfDay(_z(this.hour % 12), _z(this.hour),
|
return L10n.of(context).timeOfDay(
|
||||||
_z(this.minute), this.hour > 11 ? 'pm' : 'am');
|
_z(hour % 12), _z(hour), _z(minute), hour > 11 ? 'pm' : 'am');
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns [localizedTimeOfDay()] if the ChatTime is today, the name of the week
|
/// Returns [localizedTimeOfDay()] if the ChatTime is today, the name of the week
|
||||||
/// day if the ChatTime is this week and a date string else.
|
/// day if the ChatTime is this week and a date string else.
|
||||||
String localizedTimeShort(BuildContext context) {
|
String localizedTimeShort(BuildContext context) {
|
||||||
DateTime now = DateTime.now();
|
var now = DateTime.now();
|
||||||
|
|
||||||
bool sameYear = now.year == this.year;
|
var sameYear = now.year == year;
|
||||||
|
|
||||||
bool sameDay = sameYear && now.month == this.month && now.day == this.day;
|
var sameDay = sameYear && now.month == month && now.day == day;
|
||||||
|
|
||||||
bool sameWeek = sameYear &&
|
var sameWeek = sameYear &&
|
||||||
!sameDay &&
|
!sameDay &&
|
||||||
now.millisecondsSinceEpoch - this.millisecondsSinceEpoch <
|
now.millisecondsSinceEpoch - millisecondsSinceEpoch <
|
||||||
1000 * 60 * 60 * 24 * 7;
|
1000 * 60 * 60 * 24 * 7;
|
||||||
|
|
||||||
if (sameDay) {
|
if (sameDay) {
|
||||||
return localizedTimeOfDay(context);
|
return localizedTimeOfDay(context);
|
||||||
} else if (sameWeek) {
|
} else if (sameWeek) {
|
||||||
switch (this.weekday) {
|
switch (weekday) {
|
||||||
case 1:
|
case 1:
|
||||||
return L10n.of(context).monday;
|
return L10n.of(context).monday;
|
||||||
case 2:
|
case 2:
|
||||||
|
@ -73,29 +73,26 @@ extension DateTimeExtension on DateTime {
|
||||||
}
|
}
|
||||||
} else if (sameYear) {
|
} else if (sameYear) {
|
||||||
return L10n.of(context).dateWithoutYear(
|
return L10n.of(context).dateWithoutYear(
|
||||||
this.month.toString().padLeft(2, '0'),
|
month.toString().padLeft(2, '0'), day.toString().padLeft(2, '0'));
|
||||||
this.day.toString().padLeft(2, '0'));
|
|
||||||
}
|
}
|
||||||
return L10n.of(context).dateWithYear(
|
return L10n.of(context).dateWithYear(year.toString(),
|
||||||
this.year.toString(),
|
month.toString().padLeft(2, '0'), day.toString().padLeft(2, '0'));
|
||||||
this.month.toString().padLeft(2, '0'),
|
|
||||||
this.day.toString().padLeft(2, '0'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// If the DateTime is today, this returns [localizedTimeOfDay()], if not it also
|
/// If the DateTime is today, this returns [localizedTimeOfDay()], if not it also
|
||||||
/// shows the date.
|
/// shows the date.
|
||||||
/// TODO: Add localization
|
/// TODO: Add localization
|
||||||
String localizedTime(BuildContext context) {
|
String localizedTime(BuildContext context) {
|
||||||
DateTime now = DateTime.now();
|
var now = DateTime.now();
|
||||||
|
|
||||||
bool sameYear = now.year == this.year;
|
var sameYear = now.year == year;
|
||||||
|
|
||||||
bool sameDay = sameYear && now.month == this.month && now.day == this.day;
|
var sameDay = sameYear && now.month == month && now.day == day;
|
||||||
|
|
||||||
if (sameDay) return localizedTimeOfDay(context);
|
if (sameDay) return localizedTimeOfDay(context);
|
||||||
return L10n.of(context).dateAndTimeOfDay(
|
return L10n.of(context).dateAndTimeOfDay(
|
||||||
localizedTimeShort(context), localizedTimeOfDay(context));
|
localizedTimeShort(context), localizedTimeOfDay(context));
|
||||||
}
|
}
|
||||||
|
|
||||||
static String _z(int i) => i < 10 ? "0${i.toString()}" : i.toString();
|
static String _z(int i) => i < 10 ? '0${i.toString()}' : i.toString();
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ import 'package:flutter/material.dart';
|
||||||
|
|
||||||
extension LocalizedBody on Event {
|
extension LocalizedBody on Event {
|
||||||
IconData get statusIcon {
|
IconData get statusIcon {
|
||||||
switch (this.status) {
|
switch (status) {
|
||||||
case -1:
|
case -1:
|
||||||
return Icons.error_outline;
|
return Icons.error_outline;
|
||||||
case 0:
|
case 0:
|
||||||
|
@ -22,21 +22,21 @@ extension LocalizedBody on Event {
|
||||||
[MessageTypes.Image, MessageTypes.Sticker].contains(messageType) &&
|
[MessageTypes.Image, MessageTypes.Sticker].contains(messageType) &&
|
||||||
(kIsWeb ||
|
(kIsWeb ||
|
||||||
(content['info'] is Map &&
|
(content['info'] is Map &&
|
||||||
content['info']['size'] < room.client.store.maxFileSize));
|
content['info']['size'] < room.client.database.maxFileSize));
|
||||||
|
|
||||||
String get sizeString {
|
String get sizeString {
|
||||||
if (content["info"] is Map<String, dynamic> &&
|
if (content['info'] is Map<String, dynamic> &&
|
||||||
content["info"].containsKey("size")) {
|
content['info'].containsKey('size')) {
|
||||||
num size = content["info"]["size"];
|
num size = content['info']['size'];
|
||||||
if (size < 1000000) {
|
if (size < 1000000) {
|
||||||
size = size / 1000;
|
size = size / 1000;
|
||||||
return "${size.toString()}kb";
|
return '${size.toString()}kb';
|
||||||
} else if (size < 1000000000) {
|
} else if (size < 1000000000) {
|
||||||
size = size / 1000000;
|
size = size / 1000000;
|
||||||
return "${size.toString()}mb";
|
return '${size.toString()}mb';
|
||||||
} else {
|
} else {
|
||||||
size = size / 1000000000;
|
size = size / 1000000000;
|
||||||
return "${size.toString()}gb";
|
return '${size.toString()}gb';
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -1,25 +1,186 @@
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:typed_data';
|
|
||||||
|
|
||||||
import 'package:famedlysdk/famedlysdk.dart';
|
import 'package:famedlysdk/famedlysdk.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||||
import 'package:localstorage/localstorage.dart';
|
import 'package:localstorage/localstorage.dart';
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:core';
|
import 'dart:core';
|
||||||
import 'package:path/path.dart' as p;
|
import './database/shared.dart';
|
||||||
import 'package:sqflite/sqflite.dart';
|
import 'package:olm/olm.dart' as olm; // needed for migration
|
||||||
|
import 'package:random_string/random_string.dart';
|
||||||
|
|
||||||
class Store extends StoreAPI {
|
Future<Database> getDatabase(Client client, Store store) async {
|
||||||
final Client client;
|
var password = await store.getItem('database-password');
|
||||||
|
var needMigration = false;
|
||||||
|
if (password == null || password.isEmpty) {
|
||||||
|
needMigration = true;
|
||||||
|
password = randomString(255);
|
||||||
|
}
|
||||||
|
final db = constructDb(
|
||||||
|
logStatements: false,
|
||||||
|
filename: 'moor.sqlite',
|
||||||
|
password: password,
|
||||||
|
);
|
||||||
|
if (needMigration) {
|
||||||
|
await migrate(client.clientName, db, store);
|
||||||
|
await store.setItem('database-password', password);
|
||||||
|
}
|
||||||
|
return db;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> migrate(String clientName, Database db, Store store) async {
|
||||||
|
debugPrint('[Store] attempting old migration to moor...');
|
||||||
|
final oldKeys = await store.getAllItems();
|
||||||
|
if (oldKeys == null || oldKeys.isEmpty) {
|
||||||
|
debugPrint('[Store] empty store!');
|
||||||
|
return; // we are done!
|
||||||
|
}
|
||||||
|
final credentialsStr = oldKeys[clientName];
|
||||||
|
if (credentialsStr == null || credentialsStr.isEmpty) {
|
||||||
|
debugPrint('[Store] no credentials found!');
|
||||||
|
return; // no credentials
|
||||||
|
}
|
||||||
|
final Map<String, dynamic> credentials = json.decode(credentialsStr);
|
||||||
|
if (!credentials.containsKey('homeserver') ||
|
||||||
|
!credentials.containsKey('token') ||
|
||||||
|
!credentials.containsKey('userID')) {
|
||||||
|
debugPrint('[Store] invalid credentials!');
|
||||||
|
return; // invalid old store, we are done, too!
|
||||||
|
}
|
||||||
|
var clientId = 0;
|
||||||
|
final oldClient = await db.getClient(clientName);
|
||||||
|
if (oldClient == null) {
|
||||||
|
clientId = await db.insertClient(
|
||||||
|
clientName,
|
||||||
|
credentials['homeserver'],
|
||||||
|
credentials['token'],
|
||||||
|
credentials['userID'],
|
||||||
|
credentials['deviceID'],
|
||||||
|
credentials['deviceName'],
|
||||||
|
null,
|
||||||
|
credentials['olmAccount'],
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
clientId = oldClient.clientId;
|
||||||
|
await db.updateClient(
|
||||||
|
credentials['homeserver'],
|
||||||
|
credentials['token'],
|
||||||
|
credentials['userID'],
|
||||||
|
credentials['deviceID'],
|
||||||
|
credentials['deviceName'],
|
||||||
|
null,
|
||||||
|
credentials['olmAccount'],
|
||||||
|
clientId,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
await db.clearCache(clientId);
|
||||||
|
debugPrint('[Store] Inserted/updated client, clientId = ${clientId}');
|
||||||
|
await db.transaction(() async {
|
||||||
|
// alright, we stored / updated the client and have the account ID, time to import everything else!
|
||||||
|
// user_device_keys and user_device_keys_key
|
||||||
|
debugPrint('[Store] Migrating user device keys...');
|
||||||
|
final deviceKeysListString = oldKeys['${clientName}.user_device_keys'];
|
||||||
|
if (deviceKeysListString != null && deviceKeysListString.isNotEmpty) {
|
||||||
|
Map<String, dynamic> rawUserDeviceKeys =
|
||||||
|
json.decode(deviceKeysListString);
|
||||||
|
for (final entry in rawUserDeviceKeys.entries) {
|
||||||
|
final map = entry.value;
|
||||||
|
await db.storeUserDeviceKeysInfo(
|
||||||
|
clientId, map['user_id'], map['outdated']);
|
||||||
|
for (final rawKey in map['device_keys'].entries) {
|
||||||
|
final jsonVaue = rawKey.value;
|
||||||
|
await db.storeUserDeviceKey(
|
||||||
|
clientId,
|
||||||
|
jsonVaue['user_id'],
|
||||||
|
jsonVaue['device_id'],
|
||||||
|
json.encode(jsonVaue),
|
||||||
|
jsonVaue['verified'],
|
||||||
|
jsonVaue['blocked']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (final entry in oldKeys.entries) {
|
||||||
|
final key = entry.key;
|
||||||
|
final value = entry.value;
|
||||||
|
if (value == null || value.isEmpty) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// olm_sessions
|
||||||
|
final olmSessionsMatch =
|
||||||
|
RegExp(r'^\/clients\/([^\/]+)\/olm-sessions$').firstMatch(key);
|
||||||
|
if (olmSessionsMatch != null) {
|
||||||
|
if (olmSessionsMatch[1] != credentials['deviceID']) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
debugPrint('[Store] migrating olm sessions...');
|
||||||
|
final identityKey = json.decode(value);
|
||||||
|
for (final olmKey in identityKey.entries) {
|
||||||
|
final identKey = olmKey.key;
|
||||||
|
final sessions = olmKey.value;
|
||||||
|
for (final pickle in sessions) {
|
||||||
|
var sess = olm.Session();
|
||||||
|
sess.unpickle(credentials['userID'], pickle);
|
||||||
|
await db.storeOlmSession(
|
||||||
|
clientId, identKey, sess.session_id(), pickle);
|
||||||
|
sess?.free();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// outbound_group_sessions
|
||||||
|
final outboundGroupSessionsMatch = RegExp(
|
||||||
|
r'^\/clients\/([^\/]+)\/rooms\/([^\/]+)\/outbound_group_session$')
|
||||||
|
.firstMatch(key);
|
||||||
|
if (outboundGroupSessionsMatch != null) {
|
||||||
|
if (outboundGroupSessionsMatch[1] != credentials['deviceID']) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
final pickle = value;
|
||||||
|
final roomId = outboundGroupSessionsMatch[2];
|
||||||
|
debugPrint(
|
||||||
|
'[Store] Migrating outbound group sessions for room ${roomId}...');
|
||||||
|
final devicesString = oldKeys[
|
||||||
|
'/clients/${outboundGroupSessionsMatch[1]}/rooms/${roomId}/outbound_group_session_devices'];
|
||||||
|
var devices = <String>[];
|
||||||
|
if (devicesString != null) {
|
||||||
|
devices = List<String>.from(json.decode(devicesString));
|
||||||
|
}
|
||||||
|
await db.storeOutboundGroupSession(
|
||||||
|
clientId, roomId, pickle, json.encode(devices));
|
||||||
|
}
|
||||||
|
// session_keys
|
||||||
|
final sessionKeysMatch =
|
||||||
|
RegExp(r'^\/clients\/([^\/]+)\/rooms\/([^\/]+)\/session_keys$')
|
||||||
|
.firstMatch(key);
|
||||||
|
if (sessionKeysMatch != null) {
|
||||||
|
if (sessionKeysMatch[1] != credentials['deviceID']) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
final roomId = sessionKeysMatch[2];
|
||||||
|
debugPrint('[Store] Migrating session keys for room ${roomId}...');
|
||||||
|
final map = json.decode(value);
|
||||||
|
for (final entry in map.entries) {
|
||||||
|
await db.storeInboundGroupSession(
|
||||||
|
clientId,
|
||||||
|
roomId,
|
||||||
|
entry.key,
|
||||||
|
entry.value['inboundGroupSession'],
|
||||||
|
json.encode(entry.value['content']),
|
||||||
|
json.encode(entry.value['indexes']));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
class Store {
|
||||||
final LocalStorage storage;
|
final LocalStorage storage;
|
||||||
final FlutterSecureStorage secureStorage;
|
final FlutterSecureStorage secureStorage;
|
||||||
|
|
||||||
Store(this.client)
|
Store()
|
||||||
: storage = LocalStorage('LocalStorage'),
|
: storage = LocalStorage('LocalStorage'),
|
||||||
secureStorage = kIsWeb ? null : FlutterSecureStorage() {
|
secureStorage = kIsWeb ? null : FlutterSecureStorage();
|
||||||
_init();
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<dynamic> getItem(String key) async {
|
Future<dynamic> getItem(String key) async {
|
||||||
if (kIsWeb) {
|
if (kIsWeb) {
|
||||||
|
@ -49,587 +210,19 @@ class Store extends StoreAPI {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Map<String, DeviceKeysList>> getUserDeviceKeys() async {
|
Future<Map<String, dynamic>> getAllItems() async {
|
||||||
final deviceKeysListString = await getItem(_UserDeviceKeysKey);
|
if (kIsWeb) {
|
||||||
if (deviceKeysListString == null) return {};
|
|
||||||
Map<String, dynamic> rawUserDeviceKeys = json.decode(deviceKeysListString);
|
|
||||||
Map<String, DeviceKeysList> userDeviceKeys = {};
|
|
||||||
for (final entry in rawUserDeviceKeys.entries) {
|
|
||||||
userDeviceKeys[entry.key] = DeviceKeysList.fromJson(entry.value);
|
|
||||||
}
|
|
||||||
return userDeviceKeys;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> storeUserDeviceKeys(
|
|
||||||
Map<String, DeviceKeysList> userDeviceKeys) async {
|
|
||||||
await setItem(_UserDeviceKeysKey, json.encode(userDeviceKeys));
|
|
||||||
}
|
|
||||||
|
|
||||||
String get _UserDeviceKeysKey => "${client.clientName}.user_device_keys";
|
|
||||||
|
|
||||||
_init() async {
|
|
||||||
final credentialsStr = await getItem(client.clientName);
|
|
||||||
|
|
||||||
if (credentialsStr == null || credentialsStr.isEmpty) {
|
|
||||||
client.onLoginStateChanged.add(LoginState.loggedOut);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
debugPrint("[Matrix] Restoring account credentials");
|
|
||||||
final Map<String, dynamic> credentials = json.decode(credentialsStr);
|
|
||||||
if (credentials["homeserver"] == null ||
|
|
||||||
credentials["token"] == null ||
|
|
||||||
credentials["userID"] == null) {
|
|
||||||
client.onLoginStateChanged.add(LoginState.loggedOut);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
client.connect(
|
|
||||||
newDeviceID: credentials["deviceID"],
|
|
||||||
newDeviceName: credentials["deviceName"],
|
|
||||||
newHomeserver: credentials["homeserver"],
|
|
||||||
newMatrixVersions: List<String>.from(credentials["matrixVersions"] ?? []),
|
|
||||||
newToken: credentials["token"],
|
|
||||||
newUserID: credentials["userID"],
|
|
||||||
newPrevBatch: kIsWeb
|
|
||||||
? null
|
|
||||||
: (credentials["prev_batch"]?.isEmpty ?? true)
|
|
||||||
? null
|
|
||||||
: credentials["prev_batch"],
|
|
||||||
newOlmAccount: credentials["olmAccount"],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> storeClient() async {
|
|
||||||
final Map<String, dynamic> credentials = {
|
|
||||||
"deviceID": client.deviceID,
|
|
||||||
"deviceName": client.deviceName,
|
|
||||||
"homeserver": client.homeserver,
|
|
||||||
"matrixVersions": client.matrixVersions,
|
|
||||||
"token": client.accessToken,
|
|
||||||
"userID": client.userID,
|
|
||||||
"olmAccount": client.pickledOlmAccount,
|
|
||||||
};
|
|
||||||
await setItem(client.clientName, json.encode(credentials));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> clear() => kIsWeb ? storage.clear() : secureStorage.deleteAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Responsible to store all data persistent and to query objects from the
|
|
||||||
/// database.
|
|
||||||
class ExtendedStore extends Store implements ExtendedStoreAPI {
|
|
||||||
/// The maximum time that files are allowed to stay in the
|
|
||||||
/// store. By default this is are 30 days.
|
|
||||||
static const int MAX_FILE_STORING_TIME = 1 * 30 * 24 * 60 * 60 * 1000;
|
|
||||||
|
|
||||||
@override
|
|
||||||
final bool extended = true;
|
|
||||||
|
|
||||||
ExtendedStore(Client client) : super(client);
|
|
||||||
|
|
||||||
Database _db;
|
|
||||||
var txn;
|
|
||||||
|
|
||||||
/// SQLite database for all persistent data. It is recommended to extend this
|
|
||||||
/// SDK instead of writing direct queries to the database.
|
|
||||||
//Database get db => _db;
|
|
||||||
|
|
||||||
@override
|
|
||||||
_init() async {
|
|
||||||
// Open the database and migrate if necessary.
|
|
||||||
var databasePath = await getDatabasesPath();
|
|
||||||
String path = p.join(databasePath, "FluffyMatrix.db");
|
|
||||||
_db = await openDatabase(path, version: 20,
|
|
||||||
onCreate: (Database db, int version) async {
|
|
||||||
await createTables(db);
|
|
||||||
}, onUpgrade: (Database db, int oldVersion, int newVersion) async {
|
|
||||||
debugPrint(
|
|
||||||
"[Store] Migrate database from version $oldVersion to $newVersion");
|
|
||||||
if (oldVersion >= 18 && newVersion <= 20) {
|
|
||||||
await createTables(db);
|
|
||||||
} else if (oldVersion != newVersion) {
|
|
||||||
// Look for an old entry in an old clients library
|
|
||||||
List<Map> list = [];
|
|
||||||
try {
|
try {
|
||||||
list = await db.rawQuery(
|
final rawStorage = await getLocalstorage('LocalStorage');
|
||||||
"SELECT * FROM Clients WHERE client=?", [client.clientName]);
|
return json.decode(rawStorage);
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
list = [];
|
return {};
|
||||||
}
|
|
||||||
client.prevBatch = null;
|
|
||||||
await this.storePrevBatch(null);
|
|
||||||
schemes.forEach((String name, String scheme) async {
|
|
||||||
await db.execute("DROP TABLE IF EXISTS $name");
|
|
||||||
});
|
|
||||||
await createTables(db);
|
|
||||||
|
|
||||||
if (list.length == 1) {
|
|
||||||
debugPrint("[Store] Found old client from deprecated store");
|
|
||||||
var clientList = list[0];
|
|
||||||
_db = db;
|
|
||||||
client.connect(
|
|
||||||
newToken: clientList["token"],
|
|
||||||
newHomeserver: clientList["homeserver"],
|
|
||||||
newUserID: clientList["matrix_id"],
|
|
||||||
newDeviceID: clientList["device_id"],
|
|
||||||
newDeviceName: clientList["device_name"],
|
|
||||||
newMatrixVersions:
|
|
||||||
clientList["matrix_versions"].toString().split(","),
|
|
||||||
newPrevBatch: null,
|
|
||||||
);
|
|
||||||
await db.execute("DROP TABLE IF EXISTS Clients");
|
|
||||||
debugPrint(
|
|
||||||
"[Store] Restore client credentials from deprecated database of ${client.userID}");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
client.onLoginStateChanged.add(LoginState.loggedOut);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Mark all pending events as failed.
|
|
||||||
await _db.rawUpdate("UPDATE Events SET status=-1 WHERE status=0");
|
|
||||||
|
|
||||||
// Delete all stored files which are older than [MAX_FILE_STORING_TIME]
|
|
||||||
final int currentDeadline = DateTime.now().millisecondsSinceEpoch -
|
|
||||||
ExtendedStore.MAX_FILE_STORING_TIME;
|
|
||||||
await _db.rawDelete(
|
|
||||||
"DELETE From Files WHERE saved_at<?",
|
|
||||||
[currentDeadline],
|
|
||||||
);
|
|
||||||
|
|
||||||
super._init();
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> setRoomPrevBatch(String roomId, String prevBatch) async {
|
|
||||||
await txn.rawUpdate(
|
|
||||||
"UPDATE Rooms SET prev_batch=? WHERE room_id=?", [roomId, prevBatch]);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> createTables(Database db) async {
|
|
||||||
schemes.forEach((String name, String scheme) async {
|
|
||||||
await db.execute(scheme);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Clears all tables from the database.
|
|
||||||
Future<void> clear() async {
|
|
||||||
schemes.forEach((String name, String scheme) async {
|
|
||||||
await _db.rawDelete("DELETE FROM $name");
|
|
||||||
});
|
|
||||||
await super.clear();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> transaction(Function queries) async {
|
|
||||||
return _db.transaction((txnObj) async {
|
|
||||||
txn = txnObj.batch();
|
|
||||||
queries();
|
|
||||||
await txn.commit(noResult: true);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Will be automatically called on every synchronisation.
|
|
||||||
Future<void> storePrevBatch(String prevBatch) async {
|
|
||||||
final credentialsStr = await getItem(client.clientName);
|
|
||||||
if (credentialsStr == null) return;
|
|
||||||
final Map<String, dynamic> credentials = json.decode(credentialsStr);
|
|
||||||
credentials["prev_batch"] = prevBatch;
|
|
||||||
await setItem(client.clientName, json.encode(credentials));
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> storeRoomPrevBatch(Room room) async {
|
|
||||||
await _db.rawUpdate("UPDATE Rooms SET prev_batch=? WHERE room_id=?",
|
|
||||||
[room.prev_batch, room.id]);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Stores a RoomUpdate object in the database. Must be called inside of
|
|
||||||
/// [transaction].
|
|
||||||
Future<void> storeRoomUpdate(RoomUpdate roomUpdate) {
|
|
||||||
if (txn == null) return null;
|
|
||||||
// Insert the chat into the database if not exists
|
|
||||||
if (roomUpdate.membership != Membership.leave) {
|
|
||||||
txn.rawInsert(
|
|
||||||
"INSERT OR IGNORE INTO Rooms " + "VALUES(?, ?, 0, 0, '', 0, 0, '') ",
|
|
||||||
[roomUpdate.id, roomUpdate.membership.toString().split('.').last]);
|
|
||||||
} else {
|
|
||||||
txn.rawDelete("DELETE FROM Rooms WHERE room_id=? ", [roomUpdate.id]);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the notification counts and the limited timeline boolean and the summary
|
|
||||||
String updateQuery =
|
|
||||||
"UPDATE Rooms SET highlight_count=?, notification_count=?, membership=?";
|
|
||||||
List<dynamic> updateArgs = [
|
|
||||||
roomUpdate.highlight_count,
|
|
||||||
roomUpdate.notification_count,
|
|
||||||
roomUpdate.membership.toString().split('.').last
|
|
||||||
];
|
|
||||||
if (roomUpdate.summary?.mJoinedMemberCount != null) {
|
|
||||||
updateQuery += ", joined_member_count=?";
|
|
||||||
updateArgs.add(roomUpdate.summary.mJoinedMemberCount);
|
|
||||||
}
|
|
||||||
if (roomUpdate.summary?.mInvitedMemberCount != null) {
|
|
||||||
updateQuery += ", invited_member_count=?";
|
|
||||||
updateArgs.add(roomUpdate.summary.mInvitedMemberCount);
|
|
||||||
}
|
|
||||||
if (roomUpdate.summary?.mHeroes != null) {
|
|
||||||
updateQuery += ", heroes=?";
|
|
||||||
updateArgs.add(roomUpdate.summary.mHeroes.join(","));
|
|
||||||
}
|
|
||||||
updateQuery += " WHERE room_id=?";
|
|
||||||
updateArgs.add(roomUpdate.id);
|
|
||||||
txn.rawUpdate(updateQuery, updateArgs);
|
|
||||||
|
|
||||||
// Is the timeline limited? Then all previous messages should be
|
|
||||||
// removed from the database!
|
|
||||||
if (roomUpdate.limitedTimeline) {
|
|
||||||
txn.rawDelete("DELETE FROM Events WHERE room_id=?", [roomUpdate.id]);
|
|
||||||
txn.rawUpdate("UPDATE Rooms SET prev_batch=? WHERE room_id=?",
|
|
||||||
[roomUpdate.prev_batch, roomUpdate.id]);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Stores an UserUpdate object in the database. Must be called inside of
|
|
||||||
/// [transaction].
|
|
||||||
Future<void> storeUserEventUpdate(UserUpdate userUpdate) {
|
|
||||||
if (txn == null) return null;
|
|
||||||
if (userUpdate.type == "account_data") {
|
|
||||||
txn.rawInsert("INSERT OR REPLACE INTO AccountData VALUES(?, ?)", [
|
|
||||||
userUpdate.eventType,
|
|
||||||
json.encode(userUpdate.content["content"]),
|
|
||||||
]);
|
|
||||||
} else if (userUpdate.type == "presence") {
|
|
||||||
txn.rawInsert("INSERT OR REPLACE INTO Presences VALUES(?, ?, ?)", [
|
|
||||||
userUpdate.eventType,
|
|
||||||
userUpdate.content["sender"],
|
|
||||||
json.encode(userUpdate.content["content"]),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<dynamic> redactMessage(EventUpdate eventUpdate) async {
|
|
||||||
List<Map<String, dynamic>> res = await _db.rawQuery(
|
|
||||||
"SELECT * FROM Events WHERE event_id=?",
|
|
||||||
[eventUpdate.content["redacts"]]);
|
|
||||||
if (res.length == 1) {
|
|
||||||
Event event = Event.fromJson(res[0], null);
|
|
||||||
event.setRedactionEvent(Event.fromJson(eventUpdate.content, null));
|
|
||||||
final int changes1 = await _db.rawUpdate(
|
|
||||||
"UPDATE Events SET unsigned=?, content=?, prev_content=? WHERE event_id=?",
|
|
||||||
[
|
|
||||||
json.encode(event.unsigned ?? ""),
|
|
||||||
json.encode(event.content ?? ""),
|
|
||||||
json.encode(event.prevContent ?? ""),
|
|
||||||
event.eventId,
|
|
||||||
],
|
|
||||||
);
|
|
||||||
final int changes2 = await _db.rawUpdate(
|
|
||||||
"UPDATE RoomStates SET unsigned=?, content=?, prev_content=? WHERE event_id=?",
|
|
||||||
[
|
|
||||||
json.encode(event.unsigned ?? ""),
|
|
||||||
json.encode(event.content ?? ""),
|
|
||||||
json.encode(event.prevContent ?? ""),
|
|
||||||
event.eventId,
|
|
||||||
],
|
|
||||||
);
|
|
||||||
if (changes1 == 1 && changes2 == 1) return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Stores an EventUpdate object in the database. Must be called inside of
|
|
||||||
/// [transaction].
|
|
||||||
Future<void> storeEventUpdate(EventUpdate eventUpdate) {
|
|
||||||
if (txn == null || eventUpdate.type == "ephemeral") return null;
|
|
||||||
Map<String, dynamic> eventContent = eventUpdate.content;
|
|
||||||
String type = eventUpdate.type;
|
|
||||||
String chatId = eventUpdate.roomID;
|
|
||||||
|
|
||||||
// Get the state_key for m.room.member events
|
|
||||||
String stateKey = "";
|
|
||||||
if (eventContent["state_key"] is String) {
|
|
||||||
stateKey = eventContent["state_key"];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (eventUpdate.eventType == "m.room.redaction") {
|
|
||||||
redactMessage(eventUpdate);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type == "timeline" || type == "history") {
|
|
||||||
// calculate the status
|
|
||||||
num status = 2;
|
|
||||||
if (eventContent["status"] is num) status = eventContent["status"];
|
|
||||||
|
|
||||||
// Save the event in the database
|
|
||||||
if ((status == 1 || status == -1) &&
|
|
||||||
eventContent["unsigned"] is Map<String, dynamic> &&
|
|
||||||
eventContent["unsigned"]["transaction_id"] is String) {
|
|
||||||
txn.rawUpdate(
|
|
||||||
"UPDATE Events SET status=?, event_id=? WHERE event_id=?", [
|
|
||||||
status,
|
|
||||||
eventContent["event_id"],
|
|
||||||
eventContent["unsigned"]["transaction_id"]
|
|
||||||
]);
|
|
||||||
} else {
|
|
||||||
txn.rawInsert(
|
|
||||||
"INSERT OR REPLACE INTO Events VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
|
||||||
[
|
|
||||||
eventContent["event_id"],
|
|
||||||
chatId,
|
|
||||||
eventContent["origin_server_ts"],
|
|
||||||
eventContent["sender"],
|
|
||||||
eventContent["type"],
|
|
||||||
json.encode(eventContent["unsigned"] ?? ""),
|
|
||||||
json.encode(eventContent["content"]),
|
|
||||||
json.encode(eventContent["prevContent"]),
|
|
||||||
eventContent["state_key"],
|
|
||||||
status
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Is there a transaction id? Then delete the event with this id.
|
|
||||||
if (status != -1 &&
|
|
||||||
eventUpdate.content.containsKey("unsigned") &&
|
|
||||||
eventUpdate.content["unsigned"]["transaction_id"] is String) {
|
|
||||||
txn.rawDelete("DELETE FROM Events WHERE event_id=?",
|
|
||||||
[eventUpdate.content["unsigned"]["transaction_id"]]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
if (type == "history") return null;
|
return await secureStorage.readAll();
|
||||||
|
} catch (_) {
|
||||||
if (type != "account_data") {
|
return {};
|
||||||
final String now = DateTime.now().millisecondsSinceEpoch.toString();
|
|
||||||
txn.rawInsert(
|
|
||||||
"INSERT OR REPLACE INTO RoomStates VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
|
||||||
[
|
|
||||||
eventContent["event_id"] ?? now,
|
|
||||||
chatId,
|
|
||||||
eventContent["origin_server_ts"] ?? now,
|
|
||||||
eventContent["sender"],
|
|
||||||
stateKey,
|
|
||||||
json.encode(eventContent["unsigned"] ?? ""),
|
|
||||||
json.encode(eventContent["prev_content"] ?? ""),
|
|
||||||
eventContent["type"],
|
|
||||||
json.encode(eventContent["content"]),
|
|
||||||
]);
|
|
||||||
} else if (type == "account_data") {
|
|
||||||
txn.rawInsert("INSERT OR REPLACE INTO RoomAccountData VALUES(?, ?, ?)", [
|
|
||||||
eventContent["type"],
|
|
||||||
chatId,
|
|
||||||
json.encode(eventContent["content"]),
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a User object by a given Matrix ID and a Room.
|
|
||||||
Future<User> getUser({String matrixID, Room room}) async {
|
|
||||||
List<Map<String, dynamic>> res = await _db.rawQuery(
|
|
||||||
"SELECT * FROM RoomStates WHERE state_key=? AND room_id=?",
|
|
||||||
[matrixID, room.id]);
|
|
||||||
if (res.length != 1) return null;
|
|
||||||
return Event.fromJson(res[0], room).asUser;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a list of events for the given room and sets all participants.
|
|
||||||
Future<List<Event>> getEventList(Room room) async {
|
|
||||||
List<Map<String, dynamic>> eventRes = await _db.rawQuery(
|
|
||||||
"SELECT * " +
|
|
||||||
" FROM Events " +
|
|
||||||
" WHERE room_id=?" +
|
|
||||||
" GROUP BY event_id " +
|
|
||||||
" ORDER BY origin_server_ts DESC",
|
|
||||||
[room.id]);
|
|
||||||
|
|
||||||
List<Event> eventList = [];
|
|
||||||
|
|
||||||
for (num i = 0; i < eventRes.length; i++) {
|
|
||||||
eventList.add(Event.fromJson(eventRes[i], room));
|
|
||||||
}
|
|
||||||
|
|
||||||
return eventList;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns all rooms, the client is participating. Excludes left rooms.
|
|
||||||
Future<List<Room>> getRoomList({bool onlyLeft = false}) async {
|
|
||||||
List<Map<String, dynamic>> res = await _db.rawQuery("SELECT * " +
|
|
||||||
" FROM Rooms" +
|
|
||||||
" WHERE membership" +
|
|
||||||
(onlyLeft ? "=" : "!=") +
|
|
||||||
"'leave' " +
|
|
||||||
" GROUP BY room_id ");
|
|
||||||
List<Map<String, dynamic>> resStates = await _db.rawQuery("SELECT * FROM RoomStates WHERE type IS NOT NULL");
|
|
||||||
List<Map<String, dynamic>> resAccountData = await _db.rawQuery("SELECT * FROM RoomAccountData");
|
|
||||||
List<Room> roomList = [];
|
|
||||||
for (num i = 0; i < res.length; i++) {
|
|
||||||
Room room = await Room.getRoomFromTableRow(
|
|
||||||
res[i],
|
|
||||||
client,
|
|
||||||
states: Future.value(resStates.where((r) => r["room_id"] == res[i]["room_id"]).toList()),
|
|
||||||
roomAccountData: Future.value(resAccountData.where((r) => r["room_id"] == res[i]["room_id"]).toList()),
|
|
||||||
);
|
|
||||||
roomList.add(room);
|
|
||||||
}
|
|
||||||
return roomList;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<List<Map<String, dynamic>>> getStatesFromRoomId(String id) async {
|
|
||||||
return _db.rawQuery(
|
|
||||||
"SELECT * FROM RoomStates WHERE room_id=? AND type IS NOT NULL", [id]);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<List<Map<String, dynamic>>> getAccountDataFromRoomId(String id) async {
|
|
||||||
return _db.rawQuery("SELECT * FROM RoomAccountData WHERE room_id=?", [id]);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> resetNotificationCount(String roomID) async {
|
|
||||||
await _db.rawDelete(
|
|
||||||
"UPDATE Rooms SET notification_count=0, highlight_count=0 WHERE room_id=?",
|
|
||||||
[roomID]);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> forgetRoom(String roomID) async {
|
|
||||||
await _db.rawDelete("DELETE FROM Rooms WHERE room_id=?", [roomID]);
|
|
||||||
await _db.rawDelete("DELETE FROM Events WHERE room_id=?", [roomID]);
|
|
||||||
await _db.rawDelete("DELETE FROM RoomStates WHERE room_id=?", [roomID]);
|
|
||||||
await _db
|
|
||||||
.rawDelete("DELETE FROM RoomAccountData WHERE room_id=?", [roomID]);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Searches for the event in the store.
|
|
||||||
Future<Event> getEventById(String eventID, Room room) async {
|
|
||||||
List<Map<String, dynamic>> res = await _db.rawQuery(
|
|
||||||
"SELECT * FROM Events WHERE event_id=? AND room_id=?",
|
|
||||||
[eventID, room.id]);
|
|
||||||
if (res.isEmpty) return null;
|
|
||||||
return Event.fromJson(res[0], room);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<Map<String, AccountData>> getAccountData() async {
|
|
||||||
Map<String, AccountData> newAccountData = {};
|
|
||||||
List<Map<String, dynamic>> rawAccountData =
|
|
||||||
await _db.rawQuery("SELECT * FROM AccountData");
|
|
||||||
for (int i = 0; i < rawAccountData.length; i++) {
|
|
||||||
newAccountData[rawAccountData[i]["type"]] =
|
|
||||||
AccountData.fromJson(rawAccountData[i]);
|
|
||||||
}
|
|
||||||
return newAccountData;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<Map<String, Presence>> getPresences() async {
|
|
||||||
Map<String, Presence> newPresences = {};
|
|
||||||
List<Map<String, dynamic>> rawPresences =
|
|
||||||
await _db.rawQuery("SELECT * FROM Presences");
|
|
||||||
for (int i = 0; i < rawPresences.length; i++) {
|
|
||||||
Map<String, dynamic> rawPresence = {
|
|
||||||
"sender": rawPresences[i]["sender"],
|
|
||||||
"content": json.decode(rawPresences[i]["content"]),
|
|
||||||
};
|
|
||||||
newPresences[rawPresences[i]["sender"]] = Presence.fromJson(rawPresence);
|
|
||||||
}
|
|
||||||
return newPresences;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future removeEvent(String eventId) async {
|
|
||||||
assert(eventId != "");
|
|
||||||
await _db.rawDelete("DELETE FROM Events WHERE event_id=?", [eventId]);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> storeFile(Uint8List bytes, String mxcUri) async {
|
|
||||||
await _db.rawInsert(
|
|
||||||
"INSERT OR REPLACE INTO Files VALUES(?, ?, ?)",
|
|
||||||
[mxcUri, bytes, DateTime.now().millisecondsSinceEpoch],
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<Uint8List> getFile(String mxcUri) async {
|
|
||||||
List<Map<String, dynamic>> res = await _db.rawQuery(
|
|
||||||
"SELECT * FROM Files WHERE mxc_uri=?",
|
|
||||||
[mxcUri],
|
|
||||||
);
|
|
||||||
if (res.isEmpty) return null;
|
|
||||||
return res.first["bytes"];
|
|
||||||
}
|
|
||||||
|
|
||||||
static final Map<String, String> schemes = {
|
|
||||||
/// The database scheme for the Room class.
|
|
||||||
'Rooms': 'CREATE TABLE IF NOT EXISTS Rooms(' +
|
|
||||||
'room_id TEXT PRIMARY KEY, ' +
|
|
||||||
'membership TEXT, ' +
|
|
||||||
'highlight_count INTEGER, ' +
|
|
||||||
'notification_count INTEGER, ' +
|
|
||||||
'prev_batch TEXT, ' +
|
|
||||||
'joined_member_count INTEGER, ' +
|
|
||||||
'invited_member_count INTEGER, ' +
|
|
||||||
'heroes TEXT, ' +
|
|
||||||
'UNIQUE(room_id))',
|
|
||||||
|
|
||||||
/// The database scheme for the TimelineEvent class.
|
|
||||||
'Events': 'CREATE TABLE IF NOT EXISTS Events(' +
|
|
||||||
'event_id TEXT PRIMARY KEY, ' +
|
|
||||||
'room_id TEXT, ' +
|
|
||||||
'origin_server_ts INTEGER, ' +
|
|
||||||
'sender TEXT, ' +
|
|
||||||
'type TEXT, ' +
|
|
||||||
'unsigned TEXT, ' +
|
|
||||||
'content TEXT, ' +
|
|
||||||
'prev_content TEXT, ' +
|
|
||||||
'state_key TEXT, ' +
|
|
||||||
"status INTEGER, " +
|
|
||||||
'UNIQUE(event_id))',
|
|
||||||
|
|
||||||
/// The database scheme for room states.
|
|
||||||
'RoomStates': 'CREATE TABLE IF NOT EXISTS RoomStates(' +
|
|
||||||
'event_id TEXT PRIMARY KEY, ' +
|
|
||||||
'room_id TEXT, ' +
|
|
||||||
'origin_server_ts INTEGER, ' +
|
|
||||||
'sender TEXT, ' +
|
|
||||||
'state_key TEXT, ' +
|
|
||||||
'unsigned TEXT, ' +
|
|
||||||
'prev_content TEXT, ' +
|
|
||||||
'type TEXT, ' +
|
|
||||||
'content TEXT, ' +
|
|
||||||
'UNIQUE(room_id,state_key,type))',
|
|
||||||
|
|
||||||
/// The database scheme for room states.
|
|
||||||
'AccountData': 'CREATE TABLE IF NOT EXISTS AccountData(' +
|
|
||||||
'type TEXT PRIMARY KEY, ' +
|
|
||||||
'content TEXT, ' +
|
|
||||||
'UNIQUE(type))',
|
|
||||||
|
|
||||||
/// The database scheme for room states.
|
|
||||||
'RoomAccountData': 'CREATE TABLE IF NOT EXISTS RoomAccountData(' +
|
|
||||||
'type TEXT, ' +
|
|
||||||
'room_id TEXT, ' +
|
|
||||||
'content TEXT, ' +
|
|
||||||
'UNIQUE(type,room_id))',
|
|
||||||
|
|
||||||
/// The database scheme for room states.
|
|
||||||
'Presences': 'CREATE TABLE IF NOT EXISTS Presences(' +
|
|
||||||
'type TEXT PRIMARY KEY, ' +
|
|
||||||
'sender TEXT, ' +
|
|
||||||
'content TEXT, ' +
|
|
||||||
'UNIQUE(sender))',
|
|
||||||
|
|
||||||
/// The database scheme for room states.
|
|
||||||
'Files': 'CREATE TABLE IF NOT EXISTS Files(' +
|
|
||||||
'mxc_uri TEXT PRIMARY KEY, ' +
|
|
||||||
'bytes BLOB, ' +
|
|
||||||
'saved_at INTEGER, ' +
|
|
||||||
'UNIQUE(mxc_uri))',
|
|
||||||
};
|
|
||||||
|
|
||||||
@override
|
|
||||||
int get maxFileSize => 1 * 1024 * 1024;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,9 +15,9 @@ import 'package:famedlysdk/famedlysdk.dart';
|
||||||
import 'famedlysdk_store.dart';
|
import 'famedlysdk_store.dart';
|
||||||
|
|
||||||
abstract class FirebaseController {
|
abstract class FirebaseController {
|
||||||
static FirebaseMessaging _firebaseMessaging = FirebaseMessaging();
|
static final FirebaseMessaging _firebaseMessaging = FirebaseMessaging();
|
||||||
static FlutterLocalNotificationsPlugin _flutterLocalNotificationsPlugin =
|
static final FlutterLocalNotificationsPlugin
|
||||||
FlutterLocalNotificationsPlugin();
|
_flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
|
||||||
static BuildContext context;
|
static BuildContext context;
|
||||||
static const String CHANNEL_ID = 'fluffychat_push';
|
static const String CHANNEL_ID = 'fluffychat_push';
|
||||||
static const String CHANNEL_NAME = 'FluffyChat push channel';
|
static const String CHANNEL_NAME = 'FluffyChat push channel';
|
||||||
|
@ -52,7 +52,7 @@ abstract class FirebaseController {
|
||||||
currentPushers.first.lang == 'en' &&
|
currentPushers.first.lang == 'en' &&
|
||||||
currentPushers.first.data.url == GATEWAY_URL &&
|
currentPushers.first.data.url == GATEWAY_URL &&
|
||||||
currentPushers.first.data.format == PUSHER_FORMAT) {
|
currentPushers.first.data.format == PUSHER_FORMAT) {
|
||||||
debugPrint("[Push] Pusher already set");
|
debugPrint('[Push] Pusher already set');
|
||||||
} else {
|
} else {
|
||||||
if (currentPushers.isNotEmpty) {
|
if (currentPushers.isNotEmpty) {
|
||||||
for (final currentPusher in currentPushers) {
|
for (final currentPusher in currentPushers) {
|
||||||
|
@ -66,16 +66,16 @@ abstract class FirebaseController {
|
||||||
currentPusher.data.url,
|
currentPusher.data.url,
|
||||||
append: true,
|
append: true,
|
||||||
);
|
);
|
||||||
debugPrint("[Push] Remove legacy pusher for this device");
|
debugPrint('[Push] Remove legacy pusher for this device');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await client.setPushers(
|
await client.setPushers(
|
||||||
token,
|
token,
|
||||||
"http",
|
'http',
|
||||||
APP_ID,
|
APP_ID,
|
||||||
clientName,
|
clientName,
|
||||||
client.deviceName,
|
client.deviceName,
|
||||||
"en",
|
'en',
|
||||||
GATEWAY_URL,
|
GATEWAY_URL,
|
||||||
append: false,
|
append: false,
|
||||||
format: PUSHER_FORMAT,
|
format: PUSHER_FORMAT,
|
||||||
|
@ -88,9 +88,9 @@ abstract class FirebaseController {
|
||||||
if (message is String) {
|
if (message is String) {
|
||||||
roomId = message;
|
roomId = message;
|
||||||
} else if (message is Map) {
|
} else if (message is Map) {
|
||||||
roomId = (message["data"] ?? message)["room_id"];
|
roomId = (message['data'] ?? message)['room_id'];
|
||||||
}
|
}
|
||||||
if (roomId?.isEmpty ?? true) throw ("Bad roomId");
|
if (roomId?.isEmpty ?? true) throw ('Bad roomId');
|
||||||
await Navigator.of(context).pushAndRemoveUntil(
|
await Navigator.of(context).pushAndRemoveUntil(
|
||||||
AppRoute.defaultRoute(
|
AppRoute.defaultRoute(
|
||||||
context,
|
context,
|
||||||
|
@ -98,7 +98,7 @@ abstract class FirebaseController {
|
||||||
),
|
),
|
||||||
(r) => r.isFirst);
|
(r) => r.isFirst);
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
BotToast.showText(text: "Failed to open chat...");
|
BotToast.showText(text: 'Failed to open chat...');
|
||||||
debugPrint(_);
|
debugPrint(_);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -121,16 +121,16 @@ abstract class FirebaseController {
|
||||||
onResume: goToRoom,
|
onResume: goToRoom,
|
||||||
onLaunch: goToRoom,
|
onLaunch: goToRoom,
|
||||||
);
|
);
|
||||||
debugPrint("[Push] Firebase initialized");
|
debugPrint('[Push] Firebase initialized');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<dynamic> _onMessage(Map<String, dynamic> message) async {
|
static Future<dynamic> _onMessage(Map<String, dynamic> message) async {
|
||||||
try {
|
try {
|
||||||
final data = message['data'] ?? message;
|
final data = message['data'] ?? message;
|
||||||
final String roomId = data["room_id"];
|
final String roomId = data['room_id'];
|
||||||
final String eventId = data["event_id"];
|
final String eventId = data['event_id'];
|
||||||
final int unread = json.decode(data["counts"])["unread"];
|
final int unread = json.decode(data['counts'])['unread'];
|
||||||
if ((roomId?.isEmpty ?? true) ||
|
if ((roomId?.isEmpty ?? true) ||
|
||||||
(eventId?.isEmpty ?? true) ||
|
(eventId?.isEmpty ?? true) ||
|
||||||
unread == 0) {
|
unread == 0) {
|
||||||
|
@ -148,10 +148,12 @@ abstract class FirebaseController {
|
||||||
if (context != null) {
|
if (context != null) {
|
||||||
client = Matrix.of(context).client;
|
client = Matrix.of(context).client;
|
||||||
} else {
|
} else {
|
||||||
final platform = kIsWeb ? "Web" : Platform.operatingSystem;
|
final platform = kIsWeb ? 'Web' : Platform.operatingSystem;
|
||||||
final clientName = "FluffyChat $platform";
|
final clientName = 'FluffyChat $platform';
|
||||||
client = Client(clientName, debug: false);
|
client = Client(clientName, debug: false);
|
||||||
client.storeAPI = ExtendedStore(client);
|
final store = Store();
|
||||||
|
client.database = await getDatabase(client, store);
|
||||||
|
client.connect();
|
||||||
await client.onLoginStateChanged.stream
|
await client.onLoginStateChanged.stream
|
||||||
.firstWhere((l) => l == LoginState.logged)
|
.firstWhere((l) => l == LoginState.logged)
|
||||||
.timeout(
|
.timeout(
|
||||||
|
@ -160,7 +162,7 @@ abstract class FirebaseController {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the room
|
// Get the room
|
||||||
Room room = client.getRoomById(roomId);
|
var room = client.getRoomById(roomId);
|
||||||
if (room == null) {
|
if (room == null) {
|
||||||
await client.onRoomUpdate.stream
|
await client.onRoomUpdate.stream
|
||||||
.where((u) => u.id == roomId)
|
.where((u) => u.id == roomId)
|
||||||
|
@ -171,10 +173,10 @@ abstract class FirebaseController {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the event
|
// Get the event
|
||||||
Event event = await client.store.getEventById(eventId, room);
|
var event = await client.database.getEventById(client.id, eventId, room);
|
||||||
if (event == null) {
|
if (event == null) {
|
||||||
final EventUpdate eventUpdate = await client.onEvent.stream
|
final eventUpdate = await client.onEvent.stream
|
||||||
.where((u) => u.content["event_id"] == eventId)
|
.where((u) => u.content['event_id'] == eventId)
|
||||||
.first
|
.first
|
||||||
.timeout(Duration(seconds: 5));
|
.timeout(Duration(seconds: 5));
|
||||||
event = Event.fromJson(eventUpdate.content, room);
|
event = Event.fromJson(eventUpdate.content, room);
|
||||||
|
@ -182,18 +184,18 @@ abstract class FirebaseController {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Count all unread events
|
// Count all unread events
|
||||||
int unreadEvents = 0;
|
var unreadEvents = 0;
|
||||||
client.rooms
|
client.rooms
|
||||||
.forEach((Room room) => unreadEvents += room.notificationCount);
|
.forEach((Room room) => unreadEvents += room.notificationCount);
|
||||||
|
|
||||||
// Calculate title
|
// Calculate title
|
||||||
final String title = unread > 1
|
final title = unread > 1
|
||||||
? i18n.unreadMessagesInChats(
|
? i18n.unreadMessagesInChats(
|
||||||
unreadEvents.toString(), unread.toString())
|
unreadEvents.toString(), unread.toString())
|
||||||
: i18n.unreadMessages(unreadEvents.toString());
|
: i18n.unreadMessages(unreadEvents.toString());
|
||||||
|
|
||||||
// Calculate the body
|
// Calculate the body
|
||||||
final String body = event.getLocalizedBody(
|
final body = event.getLocalizedBody(
|
||||||
i18n,
|
i18n,
|
||||||
withSenderNamePrefix: true,
|
withSenderNamePrefix: true,
|
||||||
hideReply: true,
|
hideReply: true,
|
||||||
|
@ -238,7 +240,7 @@ abstract class FirebaseController {
|
||||||
0, room.getLocalizedDisplayname(i18n), body, platformChannelSpecifics,
|
0, room.getLocalizedDisplayname(i18n), body, platformChannelSpecifics,
|
||||||
payload: roomId);
|
payload: roomId);
|
||||||
} catch (exception) {
|
} catch (exception) {
|
||||||
debugPrint("[Push] Error while processing notification: " +
|
debugPrint('[Push] Error while processing notification: ' +
|
||||||
exception.toString());
|
exception.toString());
|
||||||
await _showDefaultNotification(message);
|
await _showDefaultNotification(message);
|
||||||
}
|
}
|
||||||
|
@ -248,8 +250,7 @@ abstract class FirebaseController {
|
||||||
static Future<dynamic> _showDefaultNotification(
|
static Future<dynamic> _showDefaultNotification(
|
||||||
Map<String, dynamic> message) async {
|
Map<String, dynamic> message) async {
|
||||||
try {
|
try {
|
||||||
FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
|
var flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
|
||||||
FlutterLocalNotificationsPlugin();
|
|
||||||
// Init notifications framework
|
// Init notifications framework
|
||||||
var initializationSettingsAndroid =
|
var initializationSettingsAndroid =
|
||||||
AndroidInitializationSettings('notifications_icon');
|
AndroidInitializationSettings('notifications_icon');
|
||||||
|
@ -261,10 +262,10 @@ abstract class FirebaseController {
|
||||||
|
|
||||||
// Notification data and matrix data
|
// Notification data and matrix data
|
||||||
Map<dynamic, dynamic> data = message['data'] ?? message;
|
Map<dynamic, dynamic> data = message['data'] ?? message;
|
||||||
String eventID = data["event_id"];
|
String eventID = data['event_id'];
|
||||||
String roomID = data["room_id"];
|
String roomID = data['room_id'];
|
||||||
final int unread = data.containsKey("counts")
|
final int unread = data.containsKey('counts')
|
||||||
? json.decode(data["counts"])["unread"]
|
? json.decode(data['counts'])['unread']
|
||||||
: 1;
|
: 1;
|
||||||
await flutterLocalNotificationsPlugin.cancelAll();
|
await flutterLocalNotificationsPlugin.cancelAll();
|
||||||
if (unread == 0 || roomID == null || eventID == null) {
|
if (unread == 0 || roomID == null || eventID == null) {
|
||||||
|
@ -278,12 +279,12 @@ abstract class FirebaseController {
|
||||||
var iOSPlatformChannelSpecifics = IOSNotificationDetails();
|
var iOSPlatformChannelSpecifics = IOSNotificationDetails();
|
||||||
var platformChannelSpecifics = NotificationDetails(
|
var platformChannelSpecifics = NotificationDetails(
|
||||||
androidPlatformChannelSpecifics, iOSPlatformChannelSpecifics);
|
androidPlatformChannelSpecifics, iOSPlatformChannelSpecifics);
|
||||||
final String title = l10n.unreadChats(unread.toString());
|
final title = l10n.unreadChats(unread.toString());
|
||||||
await flutterLocalNotificationsPlugin.show(
|
await flutterLocalNotificationsPlugin.show(
|
||||||
1, title, l10n.openAppToReadMessages, platformChannelSpecifics,
|
1, title, l10n.openAppToReadMessages, platformChannelSpecifics,
|
||||||
payload: roomID);
|
payload: roomID);
|
||||||
} catch (exception) {
|
} catch (exception) {
|
||||||
debugPrint("[Push] Error while processing background notification: " +
|
debugPrint('[Push] Error while processing background notification: ' +
|
||||||
exception.toString());
|
exception.toString());
|
||||||
}
|
}
|
||||||
return Future<void>.value();
|
return Future<void>.value();
|
||||||
|
@ -291,10 +292,10 @@ abstract class FirebaseController {
|
||||||
|
|
||||||
static Future<String> downloadAndSaveAvatar(Uri content, Client client,
|
static Future<String> downloadAndSaveAvatar(Uri content, Client client,
|
||||||
{int width, int height}) async {
|
{int width, int height}) async {
|
||||||
final bool thumbnail = width == null && height == null ? false : true;
|
final thumbnail = width == null && height == null ? false : true;
|
||||||
final String tempDirectory = (await getTemporaryDirectory()).path;
|
final tempDirectory = (await getTemporaryDirectory()).path;
|
||||||
final String prefix = thumbnail ? "thumbnail" : "";
|
final prefix = thumbnail ? 'thumbnail' : '';
|
||||||
File file =
|
var file =
|
||||||
File('$tempDirectory/${prefix}_${content.toString().split("/").last}');
|
File('$tempDirectory/${prefix}_${content.toString().split("/").last}');
|
||||||
|
|
||||||
if (!file.existsSync()) {
|
if (!file.existsSync()) {
|
||||||
|
@ -315,7 +316,7 @@ abstract class FirebaseController {
|
||||||
IosNotificationSettings(sound: true, badge: true, alert: true));
|
IosNotificationSettings(sound: true, badge: true, alert: true));
|
||||||
_firebaseMessaging.onIosSettingsRegistered
|
_firebaseMessaging.onIosSettingsRegistered
|
||||||
.listen((IosNotificationSettings settings) {
|
.listen((IosNotificationSettings settings) {
|
||||||
debugPrint("Settings registered: $settings");
|
debugPrint('Settings registered: $settings');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,8 +15,8 @@ extension MatrixFileExtension on MatrixFile {
|
||||||
var element = html.document.createElement('a');
|
var element = html.document.createElement('a');
|
||||||
element.setAttribute(
|
element.setAttribute(
|
||||||
'href', html.Url.createObjectUrlFromBlob(html.Blob([bytes])));
|
'href', html.Url.createObjectUrlFromBlob(html.Blob([bytes])));
|
||||||
element.setAttribute('target', "_blank");
|
element.setAttribute('target', '_blank');
|
||||||
element.setAttribute('rel', "noopener");
|
element.setAttribute('rel', 'noopener');
|
||||||
element.setAttribute('download', fileName);
|
element.setAttribute('download', fileName);
|
||||||
element.setAttribute('type', mimeType);
|
element.setAttribute('type', mimeType);
|
||||||
element.style.display = 'none';
|
element.style.display = 'none';
|
||||||
|
@ -24,8 +24,8 @@ extension MatrixFileExtension on MatrixFile {
|
||||||
element.click();
|
element.click();
|
||||||
element.remove();
|
element.remove();
|
||||||
} else {
|
} else {
|
||||||
Directory tempDir = await getTemporaryDirectory();
|
var tempDir = await getTemporaryDirectory();
|
||||||
final file = File(tempDir.path + "/" + path.split("/").last);
|
final file = File(tempDir.path + '/' + path.split('/').last);
|
||||||
file.writeAsBytesSync(bytes);
|
file.writeAsBytesSync(bytes);
|
||||||
await OpenFile.open(file.path);
|
await OpenFile.open(file.path);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,9 +2,9 @@ import 'package:flutter/material.dart';
|
||||||
|
|
||||||
extension StringColor on String {
|
extension StringColor on String {
|
||||||
Color get color {
|
Color get color {
|
||||||
double number = 0.0;
|
var number = 0.0;
|
||||||
for (var i = 0; i < this.length; i++) {
|
for (var i = 0; i < length; i++) {
|
||||||
number += this.codeUnitAt(i);
|
number += codeUnitAt(i);
|
||||||
}
|
}
|
||||||
number = (number % 10) * 25.5;
|
number = (number % 10) * 25.5;
|
||||||
return HSLColor.fromAHSL(1, number, 1, 0.35).toColor();
|
return HSLColor.fromAHSL(1, number, 1, 0.35).toColor();
|
||||||
|
|
|
@ -12,7 +12,7 @@ class UrlLauncher {
|
||||||
const UrlLauncher(this.context, this.url);
|
const UrlLauncher(this.context, this.url);
|
||||||
|
|
||||||
void launchUrl() {
|
void launchUrl() {
|
||||||
if (url.startsWith("https://matrix.to/#/")) {
|
if (url.startsWith('https://matrix.to/#/')) {
|
||||||
return openMatrixToUrl();
|
return openMatrixToUrl();
|
||||||
}
|
}
|
||||||
launch(url);
|
launch(url);
|
||||||
|
@ -20,8 +20,8 @@ class UrlLauncher {
|
||||||
|
|
||||||
void openMatrixToUrl() async {
|
void openMatrixToUrl() async {
|
||||||
final matrix = Matrix.of(context);
|
final matrix = Matrix.of(context);
|
||||||
final String identifier = url.replaceAll("https://matrix.to/#/", "");
|
final identifier = url.replaceAll('https://matrix.to/#/', '');
|
||||||
if (identifier.substring(0, 1) == "#") {
|
if (identifier.substring(0, 1) == '#') {
|
||||||
final response = await SimpleDialogs(context).tryRequestWithLoadingDialog(
|
final response = await SimpleDialogs(context).tryRequestWithLoadingDialog(
|
||||||
matrix.client.joinRoomById(
|
matrix.client.joinRoomById(
|
||||||
Uri.encodeComponent(identifier),
|
Uri.encodeComponent(identifier),
|
||||||
|
@ -30,13 +30,13 @@ class UrlLauncher {
|
||||||
if (response == false) return;
|
if (response == false) return;
|
||||||
await Navigator.pushAndRemoveUntil(
|
await Navigator.pushAndRemoveUntil(
|
||||||
context,
|
context,
|
||||||
AppRoute.defaultRoute(context, ChatView(response["room_id"])),
|
AppRoute.defaultRoute(context, ChatView(response['room_id'])),
|
||||||
(r) => r.isFirst,
|
(r) => r.isFirst,
|
||||||
);
|
);
|
||||||
} else if (identifier.substring(0, 1) == "@") {
|
} else if (identifier.substring(0, 1) == '@') {
|
||||||
final User user = User(
|
final user = User(
|
||||||
identifier,
|
identifier,
|
||||||
room: Room(id: "", client: matrix.client),
|
room: Room(id: '', client: matrix.client),
|
||||||
);
|
);
|
||||||
final String roomID = await SimpleDialogs(context)
|
final String roomID = await SimpleDialogs(context)
|
||||||
.tryRequestWithLoadingDialog(user.startDirectChat());
|
.tryRequestWithLoadingDialog(user.startDirectChat());
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import 'package:famedlysdk/famedlysdk.dart';
|
|
||||||
import 'package:fluffychat/components/adaptive_page_layout.dart';
|
import 'package:fluffychat/components/adaptive_page_layout.dart';
|
||||||
import 'package:fluffychat/components/matrix.dart';
|
import 'package:fluffychat/components/matrix.dart';
|
||||||
import 'package:fluffychat/l10n/l10n.dart';
|
import 'package:fluffychat/l10n/l10n.dart';
|
||||||
|
@ -21,7 +20,7 @@ class AppInfoView extends StatelessWidget {
|
||||||
class AppInfo extends StatelessWidget {
|
class AppInfo extends StatelessWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
Client client = Matrix.of(context).client;
|
var client = Matrix.of(context).client;
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(L10n.of(context).accountInformations),
|
title: Text(L10n.of(context).accountInformations),
|
||||||
|
@ -29,43 +28,39 @@ class AppInfo extends StatelessWidget {
|
||||||
body: ListView(
|
body: ListView(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text(L10n.of(context).yourOwnUsername + ":"),
|
title: Text(L10n.of(context).yourOwnUsername + ':'),
|
||||||
subtitle: Text(client.userID),
|
subtitle: Text(client.userID),
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text("Homeserver:"),
|
title: Text('Homeserver:'),
|
||||||
subtitle: Text(client.homeserver),
|
subtitle: Text(client.homeserver),
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text("Supported versions:"),
|
title: Text('Device name:'),
|
||||||
subtitle: Text(client.matrixVersions.toString()),
|
|
||||||
),
|
|
||||||
ListTile(
|
|
||||||
title: Text("Device name:"),
|
|
||||||
subtitle: Text(client.deviceName),
|
subtitle: Text(client.deviceName),
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text("Device ID:"),
|
title: Text('Device ID:'),
|
||||||
subtitle: Text(client.deviceID),
|
subtitle: Text(client.deviceID),
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text("Encryption enabled:"),
|
title: Text('Encryption enabled:'),
|
||||||
subtitle: Text(client.encryptionEnabled.toString()),
|
subtitle: Text(client.encryptionEnabled.toString()),
|
||||||
),
|
),
|
||||||
if (client.encryptionEnabled)
|
if (client.encryptionEnabled)
|
||||||
Column(
|
Column(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text("Your public fingerprint key:"),
|
title: Text('Your public fingerprint key:'),
|
||||||
subtitle: Text(client.fingerprintKey.beautified),
|
subtitle: Text(client.fingerprintKey.beautified),
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text("Your public identity key:"),
|
title: Text('Your public identity key:'),
|
||||||
subtitle: Text(client.identityKey.beautified),
|
subtitle: Text(client.identityKey.beautified),
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text("LibOlm version:"),
|
title: Text('LibOlm version:'),
|
||||||
subtitle: Text(olm.get_library_version().join(".")),
|
subtitle: Text(olm.get_library_version().join('.')),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
|
@ -44,7 +44,7 @@ class _ArchiveState extends State<Archive> {
|
||||||
),
|
),
|
||||||
secondScaffold: Scaffold(
|
secondScaffold: Scaffold(
|
||||||
body: Center(
|
body: Center(
|
||||||
child: Image.asset("assets/logo.png", width: 100, height: 100),
|
child: Image.asset('assets/logo.png', width: 100, height: 100),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
primaryPage: FocusPage.FIRST,
|
primaryPage: FocusPage.FIRST,
|
||||||
|
|
|
@ -14,8 +14,9 @@ class AuthWebView extends StatelessWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final String url = Matrix.of(context).client.homeserver +
|
final url =
|
||||||
"/_matrix/client/r0/auth/$authType/fallback/web?session=$session";
|
'/_matrix/client/r0/auth/$authType/fallback/web?session=$session' +
|
||||||
|
Matrix.of(context).client.homeserver;
|
||||||
if (kIsWeb) launch(url);
|
if (kIsWeb) launch(url);
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
|
|
|
@ -55,7 +55,7 @@ class _ChatState extends State<_Chat> {
|
||||||
|
|
||||||
MatrixState matrix;
|
MatrixState matrix;
|
||||||
|
|
||||||
String seenByText = "";
|
String seenByText = '';
|
||||||
|
|
||||||
final ScrollController _scrollController = ScrollController();
|
final ScrollController _scrollController = ScrollController();
|
||||||
|
|
||||||
|
@ -77,15 +77,19 @@ class _ChatState extends State<_Chat> {
|
||||||
|
|
||||||
final int _loadHistoryCount = 100;
|
final int _loadHistoryCount = 100;
|
||||||
|
|
||||||
String inputText = "";
|
String inputText = '';
|
||||||
|
|
||||||
bool get _canLoadMore => timeline.events.last.type != EventTypes.RoomCreate;
|
bool get _canLoadMore => timeline.events.last.type != EventTypes.RoomCreate;
|
||||||
|
|
||||||
void requestHistory() async {
|
void requestHistory() async {
|
||||||
if (_canLoadMore) {
|
if (_canLoadMore) {
|
||||||
setState(() => this._loadingHistory = true);
|
setState(() => _loadingHistory = true);
|
||||||
|
try {
|
||||||
await timeline.requestHistory(historyCount: _loadHistoryCount);
|
await timeline.requestHistory(historyCount: _loadHistoryCount);
|
||||||
if (mounted) setState(() => this._loadingHistory = false);
|
} catch (e) {
|
||||||
|
debugPrint('Error loading history: ' + e.toString());
|
||||||
|
}
|
||||||
|
if (mounted) setState(() => _loadingHistory = false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,9 +118,9 @@ class _ChatState extends State<_Chat> {
|
||||||
void updateView() {
|
void updateView() {
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
|
|
||||||
String seenByText = "";
|
var seenByText = '';
|
||||||
if (timeline.events.isNotEmpty) {
|
if (timeline.events.isNotEmpty) {
|
||||||
List lastReceipts = List.from(timeline.events.first.receipts);
|
var lastReceipts = List.from(timeline.events.first.receipts);
|
||||||
lastReceipts.removeWhere((r) =>
|
lastReceipts.removeWhere((r) =>
|
||||||
r.user.id == room.client.userID ||
|
r.user.id == room.client.userID ||
|
||||||
r.user.id == timeline.events.first.senderId);
|
r.user.id == timeline.events.first.senderId);
|
||||||
|
@ -147,7 +151,7 @@ class _ChatState extends State<_Chat> {
|
||||||
unawaited(room.sendReadReceipt(timeline.events.first.eventId));
|
unawaited(room.sendReadReceipt(timeline.events.first.eventId));
|
||||||
}
|
}
|
||||||
if (timeline.events.length < _loadHistoryCount) {
|
if (timeline.events.length < _loadHistoryCount) {
|
||||||
this.requestHistory();
|
requestHistory();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
updateView();
|
updateView();
|
||||||
|
@ -158,7 +162,7 @@ class _ChatState extends State<_Chat> {
|
||||||
void dispose() {
|
void dispose() {
|
||||||
timeline?.cancelSubscriptions();
|
timeline?.cancelSubscriptions();
|
||||||
timeline = null;
|
timeline = null;
|
||||||
matrix.activeRoomId = "";
|
matrix.activeRoomId = '';
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -167,12 +171,12 @@ class _ChatState extends State<_Chat> {
|
||||||
void send() {
|
void send() {
|
||||||
if (sendController.text.isEmpty) return;
|
if (sendController.text.isEmpty) return;
|
||||||
room.sendTextEvent(sendController.text, inReplyTo: replyEvent);
|
room.sendTextEvent(sendController.text, inReplyTo: replyEvent);
|
||||||
sendController.text = "";
|
sendController.text = '';
|
||||||
if (replyEvent != null) {
|
if (replyEvent != null) {
|
||||||
setState(() => replyEvent = null);
|
setState(() => replyEvent = null);
|
||||||
}
|
}
|
||||||
|
|
||||||
setState(() => inputText = "");
|
setState(() => inputText = '');
|
||||||
}
|
}
|
||||||
|
|
||||||
void sendFileAction(BuildContext context) async {
|
void sendFileAction(BuildContext context) async {
|
||||||
|
@ -180,7 +184,7 @@ class _ChatState extends State<_Chat> {
|
||||||
BotToast.showText(text: L10n.of(context).notSupportedInWeb);
|
BotToast.showText(text: L10n.of(context).notSupportedInWeb);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
File file = await FilePicker.getFile();
|
var file = await FilePicker.getFile();
|
||||||
if (file == null) return;
|
if (file == null) return;
|
||||||
await SimpleDialogs(context).tryRequestWithLoadingDialog(
|
await SimpleDialogs(context).tryRequestWithLoadingDialog(
|
||||||
room.sendFileEvent(
|
room.sendFileEvent(
|
||||||
|
@ -194,7 +198,7 @@ class _ChatState extends State<_Chat> {
|
||||||
BotToast.showText(text: L10n.of(context).notSupportedInWeb);
|
BotToast.showText(text: L10n.of(context).notSupportedInWeb);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
File file = await ImagePicker.pickImage(
|
var file = await ImagePicker.pickImage(
|
||||||
source: ImageSource.gallery,
|
source: ImageSource.gallery,
|
||||||
imageQuality: 50,
|
imageQuality: 50,
|
||||||
maxWidth: 1600,
|
maxWidth: 1600,
|
||||||
|
@ -212,7 +216,7 @@ class _ChatState extends State<_Chat> {
|
||||||
BotToast.showText(text: L10n.of(context).notSupportedInWeb);
|
BotToast.showText(text: L10n.of(context).notSupportedInWeb);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
File file = await ImagePicker.pickImage(
|
var file = await ImagePicker.pickImage(
|
||||||
source: ImageSource.camera,
|
source: ImageSource.camera,
|
||||||
imageQuality: 50,
|
imageQuality: 50,
|
||||||
maxWidth: 1600,
|
maxWidth: 1600,
|
||||||
|
@ -233,7 +237,7 @@ class _ChatState extends State<_Chat> {
|
||||||
onFinished: (r) => result = r,
|
onFinished: (r) => result = r,
|
||||||
));
|
));
|
||||||
if (result == null) return;
|
if (result == null) return;
|
||||||
final File audioFile = File(result);
|
final audioFile = File(result);
|
||||||
await SimpleDialogs(context).tryRequestWithLoadingDialog(
|
await SimpleDialogs(context).tryRequestWithLoadingDialog(
|
||||||
room.sendAudioEvent(
|
room.sendAudioEvent(
|
||||||
MatrixFile(bytes: audioFile.readAsBytesSync(), path: audioFile.path),
|
MatrixFile(bytes: audioFile.readAsBytesSync(), path: audioFile.path),
|
||||||
|
@ -242,12 +246,12 @@ class _ChatState extends State<_Chat> {
|
||||||
}
|
}
|
||||||
|
|
||||||
String _getSelectedEventString(BuildContext context) {
|
String _getSelectedEventString(BuildContext context) {
|
||||||
String copyString = "";
|
var copyString = '';
|
||||||
if (selectedEvents.length == 1) {
|
if (selectedEvents.length == 1) {
|
||||||
return selectedEvents.first.getLocalizedBody(L10n.of(context));
|
return selectedEvents.first.getLocalizedBody(L10n.of(context));
|
||||||
}
|
}
|
||||||
for (Event event in selectedEvents) {
|
for (var event in selectedEvents) {
|
||||||
if (copyString.isNotEmpty) copyString += "\n\n";
|
if (copyString.isNotEmpty) copyString += '\n\n';
|
||||||
copyString +=
|
copyString +=
|
||||||
event.getLocalizedBody(L10n.of(context), withSenderNamePrefix: true);
|
event.getLocalizedBody(L10n.of(context), withSenderNamePrefix: true);
|
||||||
}
|
}
|
||||||
|
@ -260,12 +264,12 @@ class _ChatState extends State<_Chat> {
|
||||||
}
|
}
|
||||||
|
|
||||||
void redactEventsAction(BuildContext context) async {
|
void redactEventsAction(BuildContext context) async {
|
||||||
bool confirmed = await SimpleDialogs(context).askConfirmation(
|
var confirmed = await SimpleDialogs(context).askConfirmation(
|
||||||
titleText: L10n.of(context).messageWillBeRemovedWarning,
|
titleText: L10n.of(context).messageWillBeRemovedWarning,
|
||||||
confirmText: L10n.of(context).remove,
|
confirmText: L10n.of(context).remove,
|
||||||
);
|
);
|
||||||
if (!confirmed) return;
|
if (!confirmed) return;
|
||||||
for (Event event in selectedEvents) {
|
for (var event in selectedEvents) {
|
||||||
await SimpleDialogs(context).tryRequestWithLoadingDialog(
|
await SimpleDialogs(context).tryRequestWithLoadingDialog(
|
||||||
event.status > 0 ? event.redact() : event.remove());
|
event.status > 0 ? event.redact() : event.remove());
|
||||||
}
|
}
|
||||||
|
@ -273,7 +277,7 @@ class _ChatState extends State<_Chat> {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool get canRedactSelectedEvents {
|
bool get canRedactSelectedEvents {
|
||||||
for (Event event in selectedEvents) {
|
for (var event in selectedEvents) {
|
||||||
if (event.canRedact == false) return false;
|
if (event.canRedact == false) return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
@ -284,8 +288,8 @@ class _ChatState extends State<_Chat> {
|
||||||
Matrix.of(context).shareContent = selectedEvents.first.content;
|
Matrix.of(context).shareContent = selectedEvents.first.content;
|
||||||
} else {
|
} else {
|
||||||
Matrix.of(context).shareContent = {
|
Matrix.of(context).shareContent = {
|
||||||
"msgtype": "m.text",
|
'msgtype': 'm.text',
|
||||||
"body": _getSelectedEventString(context),
|
'body': _getSelectedEventString(context),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
setState(() => selectedEvents.clear());
|
setState(() => selectedEvents.clear());
|
||||||
|
@ -308,7 +312,7 @@ class _ChatState extends State<_Chat> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
matrix = Matrix.of(context);
|
matrix = Matrix.of(context);
|
||||||
Client client = matrix.client;
|
var client = matrix.client;
|
||||||
room ??= client.getRoomById(widget.id);
|
room ??= client.getRoomById(widget.id);
|
||||||
if (room == null) {
|
if (room == null) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
|
@ -326,8 +330,8 @@ class _ChatState extends State<_Chat> {
|
||||||
SimpleDialogs(context).tryRequestWithLoadingDialog(room.join());
|
SimpleDialogs(context).tryRequestWithLoadingDialog(room.join());
|
||||||
}
|
}
|
||||||
|
|
||||||
String typingText = "";
|
var typingText = '';
|
||||||
List<User> typingUsers = room.typingUsers;
|
var typingUsers = room.typingUsers;
|
||||||
typingUsers.removeWhere((User u) => u.id == client.userID);
|
typingUsers.removeWhere((User u) => u.id == client.userID);
|
||||||
|
|
||||||
if (typingUsers.length == 1) {
|
if (typingUsers.length == 1) {
|
||||||
|
@ -616,22 +620,22 @@ class _ChatState extends State<_Chat> {
|
||||||
PopupMenuButton<String>(
|
PopupMenuButton<String>(
|
||||||
icon: Icon(Icons.add),
|
icon: Icon(Icons.add),
|
||||||
onSelected: (String choice) async {
|
onSelected: (String choice) async {
|
||||||
if (choice == "file") {
|
if (choice == 'file') {
|
||||||
sendFileAction(context);
|
sendFileAction(context);
|
||||||
} else if (choice == "image") {
|
} else if (choice == 'image') {
|
||||||
sendImageAction(context);
|
sendImageAction(context);
|
||||||
}
|
}
|
||||||
if (choice == "camera") {
|
if (choice == 'camera') {
|
||||||
openCameraAction(context);
|
openCameraAction(context);
|
||||||
}
|
}
|
||||||
if (choice == "voice") {
|
if (choice == 'voice') {
|
||||||
voiceMessageAction(context);
|
voiceMessageAction(context);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
itemBuilder: (BuildContext context) =>
|
itemBuilder: (BuildContext context) =>
|
||||||
<PopupMenuEntry<String>>[
|
<PopupMenuEntry<String>>[
|
||||||
PopupMenuItem<String>(
|
PopupMenuItem<String>(
|
||||||
value: "file",
|
value: 'file',
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
leading: CircleAvatar(
|
leading: CircleAvatar(
|
||||||
backgroundColor: Colors.green,
|
backgroundColor: Colors.green,
|
||||||
|
@ -644,7 +648,7 @@ class _ChatState extends State<_Chat> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
PopupMenuItem<String>(
|
PopupMenuItem<String>(
|
||||||
value: "image",
|
value: 'image',
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
leading: CircleAvatar(
|
leading: CircleAvatar(
|
||||||
backgroundColor: Colors.blue,
|
backgroundColor: Colors.blue,
|
||||||
|
@ -657,7 +661,7 @@ class _ChatState extends State<_Chat> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
PopupMenuItem<String>(
|
PopupMenuItem<String>(
|
||||||
value: "camera",
|
value: 'camera',
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
leading: CircleAvatar(
|
leading: CircleAvatar(
|
||||||
backgroundColor: Colors.purple,
|
backgroundColor: Colors.purple,
|
||||||
|
@ -670,7 +674,7 @@ class _ChatState extends State<_Chat> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
PopupMenuItem<String>(
|
PopupMenuItem<String>(
|
||||||
value: "voice",
|
value: 'voice',
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
leading: CircleAvatar(
|
leading: CircleAvatar(
|
||||||
backgroundColor: Colors.red,
|
backgroundColor: Colors.red,
|
||||||
|
@ -708,20 +712,20 @@ class _ChatState extends State<_Chat> {
|
||||||
border: InputBorder.none,
|
border: InputBorder.none,
|
||||||
),
|
),
|
||||||
onChanged: (String text) {
|
onChanged: (String text) {
|
||||||
this.typingCoolDown?.cancel();
|
typingCoolDown?.cancel();
|
||||||
this.typingCoolDown =
|
typingCoolDown =
|
||||||
Timer(Duration(seconds: 2), () {
|
Timer(Duration(seconds: 2), () {
|
||||||
this.typingCoolDown = null;
|
typingCoolDown = null;
|
||||||
this.currentlyTyping = false;
|
currentlyTyping = false;
|
||||||
room.sendTypingInfo(false);
|
room.sendTypingInfo(false);
|
||||||
});
|
});
|
||||||
this.typingTimeout ??=
|
typingTimeout ??=
|
||||||
Timer(Duration(seconds: 30), () {
|
Timer(Duration(seconds: 30), () {
|
||||||
this.typingTimeout = null;
|
typingTimeout = null;
|
||||||
this.currentlyTyping = false;
|
currentlyTyping = false;
|
||||||
});
|
});
|
||||||
if (!this.currentlyTyping) {
|
if (!currentlyTyping) {
|
||||||
this.currentlyTyping = true;
|
currentlyTyping = true;
|
||||||
room.sendTypingInfo(true,
|
room.sendTypingInfo(true,
|
||||||
timeout: Duration(seconds: 30)
|
timeout: Duration(seconds: 30)
|
||||||
.inMilliseconds);
|
.inMilliseconds);
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
import 'dart:io';
|
|
||||||
|
|
||||||
import 'package:famedlysdk/famedlysdk.dart';
|
import 'package:famedlysdk/famedlysdk.dart';
|
||||||
import 'package:fluffychat/components/adaptive_page_layout.dart';
|
import 'package:fluffychat/components/adaptive_page_layout.dart';
|
||||||
import 'package:fluffychat/components/chat_settings_popup_menu.dart';
|
import 'package:fluffychat/components/chat_settings_popup_menu.dart';
|
||||||
|
@ -30,11 +28,12 @@ class ChatDetails extends StatefulWidget {
|
||||||
class _ChatDetailsState extends State<ChatDetails> {
|
class _ChatDetailsState extends State<ChatDetails> {
|
||||||
List<User> members;
|
List<User> members;
|
||||||
void setDisplaynameAction(BuildContext context) async {
|
void setDisplaynameAction(BuildContext context) async {
|
||||||
final String displayname = await SimpleDialogs(context).enterText(
|
var enterText = SimpleDialogs(context).enterText(
|
||||||
titleText: L10n.of(context).changeTheNameOfTheGroup,
|
titleText: L10n.of(context).changeTheNameOfTheGroup,
|
||||||
labelText: L10n.of(context).changeTheNameOfTheGroup,
|
labelText: L10n.of(context).changeTheNameOfTheGroup,
|
||||||
hintText: widget.room.getLocalizedDisplayname(L10n.of(context)),
|
hintText: widget.room.getLocalizedDisplayname(L10n.of(context)),
|
||||||
);
|
);
|
||||||
|
final displayname = await enterText;
|
||||||
if (displayname == null) return;
|
if (displayname == null) return;
|
||||||
final success = await SimpleDialogs(context).tryRequestWithLoadingDialog(
|
final success = await SimpleDialogs(context).tryRequestWithLoadingDialog(
|
||||||
widget.room.setName(displayname),
|
widget.room.setName(displayname),
|
||||||
|
@ -45,26 +44,26 @@ class _ChatDetailsState extends State<ChatDetails> {
|
||||||
}
|
}
|
||||||
|
|
||||||
void setCanonicalAliasAction(context) async {
|
void setCanonicalAliasAction(context) async {
|
||||||
final String s = await SimpleDialogs(context).enterText(
|
final s = await SimpleDialogs(context).enterText(
|
||||||
titleText: L10n.of(context).setInvitationLink,
|
titleText: L10n.of(context).setInvitationLink,
|
||||||
labelText: L10n.of(context).setInvitationLink,
|
labelText: L10n.of(context).setInvitationLink,
|
||||||
hintText: L10n.of(context).alias.toLowerCase(),
|
hintText: L10n.of(context).alias.toLowerCase(),
|
||||||
prefixText: "#",
|
prefixText: '#',
|
||||||
suffixText: ":" + widget.room.client.userID.domain,
|
suffixText: ':' + widget.room.client.userID.domain,
|
||||||
);
|
);
|
||||||
if (s == null) return;
|
if (s == null) return;
|
||||||
final String domain = widget.room.client.userID.domain;
|
final domain = widget.room.client.userID.domain;
|
||||||
final String canonicalAlias = "%23" + s + "%3A" + domain;
|
final canonicalAlias = '%23' + s + '%3A' + domain;
|
||||||
final Event aliasEvent = widget.room.getState("m.room.aliases", domain);
|
final aliasEvent = widget.room.getState('m.room.aliases', domain);
|
||||||
final List aliases =
|
final aliases =
|
||||||
aliasEvent != null ? aliasEvent.content["aliases"] ?? [] : [];
|
aliasEvent != null ? aliasEvent.content['aliases'] ?? [] : [];
|
||||||
if (aliases.indexWhere((s) => s == canonicalAlias) == -1) {
|
if (aliases.indexWhere((s) => s == canonicalAlias) == -1) {
|
||||||
List<String> newAliases = List.from(aliases);
|
var newAliases = List<String>.from(aliases);
|
||||||
newAliases.add(canonicalAlias);
|
newAliases.add(canonicalAlias);
|
||||||
final response = await SimpleDialogs(context).tryRequestWithLoadingDialog(
|
final response = await SimpleDialogs(context).tryRequestWithLoadingDialog(
|
||||||
widget.room.client.jsonRequest(
|
widget.room.client.jsonRequest(
|
||||||
type: HTTPType.GET,
|
type: HTTPType.GET,
|
||||||
action: "/client/r0/directory/room/$canonicalAlias",
|
action: '/client/r0/directory/room/$canonicalAlias',
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
if (response == false) {
|
if (response == false) {
|
||||||
|
@ -72,8 +71,8 @@ class _ChatDetailsState extends State<ChatDetails> {
|
||||||
await SimpleDialogs(context).tryRequestWithLoadingDialog(
|
await SimpleDialogs(context).tryRequestWithLoadingDialog(
|
||||||
widget.room.client.jsonRequest(
|
widget.room.client.jsonRequest(
|
||||||
type: HTTPType.PUT,
|
type: HTTPType.PUT,
|
||||||
action: "/client/r0/directory/room/$canonicalAlias",
|
action: '/client/r0/directory/room/$canonicalAlias',
|
||||||
data: {"room_id": widget.room.id}),
|
data: {'room_id': widget.room.id}),
|
||||||
);
|
);
|
||||||
if (success == false) return;
|
if (success == false) return;
|
||||||
}
|
}
|
||||||
|
@ -82,13 +81,13 @@ class _ChatDetailsState extends State<ChatDetails> {
|
||||||
widget.room.client.jsonRequest(
|
widget.room.client.jsonRequest(
|
||||||
type: HTTPType.PUT,
|
type: HTTPType.PUT,
|
||||||
action:
|
action:
|
||||||
"/client/r0/rooms/${widget.room.id}/state/m.room.canonical_alias",
|
'/client/r0/rooms/${widget.room.id}/state/m.room.canonical_alias',
|
||||||
data: {"alias": "#$s:$domain"}),
|
data: {'alias': '#$s:$domain'}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setTopicAction(BuildContext context) async {
|
void setTopicAction(BuildContext context) async {
|
||||||
final String displayname = await SimpleDialogs(context).enterText(
|
final displayname = await SimpleDialogs(context).enterText(
|
||||||
titleText: L10n.of(context).setGroupDescription,
|
titleText: L10n.of(context).setGroupDescription,
|
||||||
labelText: L10n.of(context).setGroupDescription,
|
labelText: L10n.of(context).setGroupDescription,
|
||||||
hintText: (widget.room.topic?.isNotEmpty ?? false)
|
hintText: (widget.room.topic?.isNotEmpty ?? false)
|
||||||
|
@ -106,7 +105,7 @@ class _ChatDetailsState extends State<ChatDetails> {
|
||||||
}
|
}
|
||||||
|
|
||||||
void setAvatarAction(BuildContext context) async {
|
void setAvatarAction(BuildContext context) async {
|
||||||
final File tempFile = await ImagePicker.pickImage(
|
final tempFile = await ImagePicker.pickImage(
|
||||||
source: ImageSource.gallery,
|
source: ImageSource.gallery,
|
||||||
imageQuality: 50,
|
imageQuality: 50,
|
||||||
maxWidth: 1600,
|
maxWidth: 1600,
|
||||||
|
@ -145,9 +144,9 @@ class _ChatDetailsState extends State<ChatDetails> {
|
||||||
}
|
}
|
||||||
members ??= widget.room.getParticipants();
|
members ??= widget.room.getParticipants();
|
||||||
members.removeWhere((u) => u.membership == Membership.leave);
|
members.removeWhere((u) => u.membership == Membership.leave);
|
||||||
final int actualMembersCount =
|
final actualMembersCount =
|
||||||
widget.room.mInvitedMemberCount + widget.room.mJoinedMemberCount;
|
widget.room.mInvitedMemberCount + widget.room.mJoinedMemberCount;
|
||||||
final bool canRequestMoreMembers = members.length < actualMembersCount;
|
final canRequestMoreMembers = members.length < actualMembersCount;
|
||||||
return AdaptivePageLayout(
|
return AdaptivePageLayout(
|
||||||
primaryPage: FocusPage.SECOND,
|
primaryPage: FocusPage.SECOND,
|
||||||
firstScaffold: ChatList(
|
firstScaffold: ChatList(
|
||||||
|
@ -189,7 +188,7 @@ class _ChatDetailsState extends State<ChatDetails> {
|
||||||
backgroundColor: Theme.of(context).appBarTheme.color,
|
backgroundColor: Theme.of(context).appBarTheme.color,
|
||||||
flexibleSpace: FlexibleSpaceBar(
|
flexibleSpace: FlexibleSpaceBar(
|
||||||
background: ContentBanner(widget.room.avatar,
|
background: ContentBanner(widget.room.avatar,
|
||||||
onEdit: widget.room.canSendEvent("m.room.avatar") &&
|
onEdit: widget.room.canSendEvent('m.room.avatar') &&
|
||||||
!kIsWeb
|
!kIsWeb
|
||||||
? () => setAvatarAction(context)
|
? () => setAvatarAction(context)
|
||||||
: null),
|
: null),
|
||||||
|
@ -204,7 +203,7 @@ class _ChatDetailsState extends State<ChatDetails> {
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: widget.room.canSendEvent("m.room.topic")
|
leading: widget.room.canSendEvent('m.room.topic')
|
||||||
? CircleAvatar(
|
? CircleAvatar(
|
||||||
backgroundColor: Theme.of(context)
|
backgroundColor: Theme.of(context)
|
||||||
.scaffoldBackgroundColor,
|
.scaffoldBackgroundColor,
|
||||||
|
@ -213,7 +212,7 @@ class _ChatDetailsState extends State<ChatDetails> {
|
||||||
)
|
)
|
||||||
: null,
|
: null,
|
||||||
title: Text(
|
title: Text(
|
||||||
"${L10n.of(context).groupDescription}:",
|
'${L10n.of(context).groupDescription}:',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Theme.of(context).primaryColor,
|
color: Theme.of(context).primaryColor,
|
||||||
fontWeight: FontWeight.bold)),
|
fontWeight: FontWeight.bold)),
|
||||||
|
@ -230,7 +229,7 @@ class _ChatDetailsState extends State<ChatDetails> {
|
||||||
.color,
|
.color,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
onTap: widget.room.canSendEvent("m.room.topic")
|
onTap: widget.room.canSendEvent('m.room.topic')
|
||||||
? () => setTopicAction(context)
|
? () => setTopicAction(context)
|
||||||
: null,
|
: null,
|
||||||
),
|
),
|
||||||
|
@ -244,7 +243,7 @@ class _ChatDetailsState extends State<ChatDetails> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (widget.room.canSendEvent("m.room.name"))
|
if (widget.room.canSendEvent('m.room.name'))
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: CircleAvatar(
|
leading: CircleAvatar(
|
||||||
backgroundColor:
|
backgroundColor:
|
||||||
|
@ -259,7 +258,7 @@ class _ChatDetailsState extends State<ChatDetails> {
|
||||||
onTap: () => setDisplaynameAction(context),
|
onTap: () => setDisplaynameAction(context),
|
||||||
),
|
),
|
||||||
if (widget.room
|
if (widget.room
|
||||||
.canSendEvent("m.room.canonical_alias") &&
|
.canSendEvent('m.room.canonical_alias') &&
|
||||||
widget.room.joinRules == JoinRules.public)
|
widget.room.joinRules == JoinRules.public)
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: CircleAvatar(
|
leading: CircleAvatar(
|
||||||
|
|
|
@ -50,14 +50,14 @@ class _ChatEncryptionSettingsState extends State<ChatEncryptionSettings> {
|
||||||
if (snapshot.hasError) {
|
if (snapshot.hasError) {
|
||||||
return Center(
|
return Center(
|
||||||
child: Text(L10n.of(context).oopsSomethingWentWrong +
|
child: Text(L10n.of(context).oopsSomethingWentWrong +
|
||||||
": " +
|
': ' +
|
||||||
snapshot.error.toString()),
|
snapshot.error.toString()),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (!snapshot.hasData) {
|
if (!snapshot.hasData) {
|
||||||
return Center(child: CircularProgressIndicator());
|
return Center(child: CircularProgressIndicator());
|
||||||
}
|
}
|
||||||
final List<DeviceKeys> deviceKeys = snapshot.data;
|
final deviceKeys = snapshot.data;
|
||||||
return ListView.separated(
|
return ListView.separated(
|
||||||
separatorBuilder: (BuildContext context, int i) =>
|
separatorBuilder: (BuildContext context, int i) =>
|
||||||
Divider(height: 1),
|
Divider(height: 1),
|
||||||
|
@ -96,7 +96,7 @@ class _ChatEncryptionSettingsState extends State<ChatEncryptionSettings> {
|
||||||
),
|
),
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
deviceKeys[i]
|
deviceKeys[i]
|
||||||
.keys["ed25519:${deviceKeys[i].deviceId}"]
|
.keys['ed25519:${deviceKeys[i].deviceId}']
|
||||||
.beautified,
|
.beautified,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color:
|
color:
|
||||||
|
|
|
@ -35,7 +35,7 @@ class ChatListView extends StatelessWidget {
|
||||||
firstScaffold: ChatList(),
|
firstScaffold: ChatList(),
|
||||||
secondScaffold: Scaffold(
|
secondScaffold: Scaffold(
|
||||||
body: Center(
|
body: Center(
|
||||||
child: Image.asset("assets/logo.png", width: 100, height: 100),
|
child: Image.asset('assets/logo.png', width: 100, height: 100),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -62,7 +62,7 @@ class _ChatListState extends State<ChatList> {
|
||||||
final ScrollController _scrollController = ScrollController();
|
final ScrollController _scrollController = ScrollController();
|
||||||
|
|
||||||
Future<void> waitForFirstSync(BuildContext context) async {
|
Future<void> waitForFirstSync(BuildContext context) async {
|
||||||
Client client = Matrix.of(context).client;
|
var client = Matrix.of(context).client;
|
||||||
if (client.prevBatch?.isEmpty ?? true) {
|
if (client.prevBatch?.isEmpty ?? true) {
|
||||||
await client.onFirstSync.stream.first;
|
await client.onFirstSync.stream.first;
|
||||||
}
|
}
|
||||||
|
@ -106,7 +106,7 @@ class _ChatListState extends State<ChatList> {
|
||||||
publicRoomsResponse = newPublicRoomsResponse;
|
publicRoomsResponse = newPublicRoomsResponse;
|
||||||
if (searchController.text.isNotEmpty &&
|
if (searchController.text.isNotEmpty &&
|
||||||
searchController.text.isValidMatrixId &&
|
searchController.text.isValidMatrixId &&
|
||||||
searchController.text.sigil == "#") {
|
searchController.text.sigil == '#') {
|
||||||
publicRoomsResponse.publicRooms.add(
|
publicRoomsResponse.publicRooms.add(
|
||||||
PublicRoomEntry(
|
PublicRoomEntry(
|
||||||
aliases: [searchController.text],
|
aliases: [searchController.text],
|
||||||
|
@ -134,11 +134,11 @@ class _ChatListState extends State<ChatList> {
|
||||||
if (Navigator.of(context).canPop()) {
|
if (Navigator.of(context).canPop()) {
|
||||||
Navigator.of(context).popUntil((r) => r.isFirst);
|
Navigator.of(context).popUntil((r) => r.isFirst);
|
||||||
}
|
}
|
||||||
final File file = File(files.first.path);
|
final file = File(files.first.path);
|
||||||
|
|
||||||
Matrix.of(context).shareContent = {
|
Matrix.of(context).shareContent = {
|
||||||
"msgtype": "chat.fluffy.shared_file",
|
'msgtype': 'chat.fluffy.shared_file',
|
||||||
"file": MatrixFile(
|
'file': MatrixFile(
|
||||||
bytes: file.readAsBytesSync(),
|
bytes: file.readAsBytesSync(),
|
||||||
path: file.path,
|
path: file.path,
|
||||||
),
|
),
|
||||||
|
@ -150,13 +150,13 @@ class _ChatListState extends State<ChatList> {
|
||||||
if (Navigator.of(context).canPop()) {
|
if (Navigator.of(context).canPop()) {
|
||||||
Navigator.of(context).popUntil((r) => r.isFirst);
|
Navigator.of(context).popUntil((r) => r.isFirst);
|
||||||
}
|
}
|
||||||
if (text.startsWith("https://matrix.to/#/")) {
|
if (text.startsWith('https://matrix.to/#/')) {
|
||||||
UrlLauncher(context, text).openMatrixToUrl();
|
UrlLauncher(context, text).openMatrixToUrl();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Matrix.of(context).shareContent = {
|
Matrix.of(context).shareContent = {
|
||||||
"msgtype": "m.text",
|
'msgtype': 'm.text',
|
||||||
"body": text,
|
'body': text,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -204,8 +204,8 @@ class _ChatListState extends State<ChatList> {
|
||||||
action:
|
action:
|
||||||
'/client/r0/presence/${Matrix.of(context).client.userID}/status',
|
'/client/r0/presence/${Matrix.of(context).client.userID}/status',
|
||||||
data: {
|
data: {
|
||||||
"presence": "online",
|
'presence': 'online',
|
||||||
"status_msg": status,
|
'status_msg': status,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -288,7 +288,7 @@ class _ChatListState extends State<ChatList> {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
Share.share(L10n.of(context).inviteText(
|
Share.share(L10n.of(context).inviteText(
|
||||||
Matrix.of(context).client.userID,
|
Matrix.of(context).client.userID,
|
||||||
"https://matrix.to/#/${Matrix.of(context).client.userID}"));
|
'https://matrix.to/#/${Matrix.of(context).client.userID}'));
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -381,13 +381,13 @@ class _ChatListState extends State<ChatList> {
|
||||||
future: waitForFirstSync(context),
|
future: waitForFirstSync(context),
|
||||||
builder: (BuildContext context, snapshot) {
|
builder: (BuildContext context, snapshot) {
|
||||||
if (snapshot.hasData) {
|
if (snapshot.hasData) {
|
||||||
List<Room> rooms = List<Room>.from(
|
var rooms = List<Room>.from(
|
||||||
Matrix.of(context).client.rooms);
|
Matrix.of(context).client.rooms);
|
||||||
rooms.removeWhere((Room room) =>
|
rooms.removeWhere((Room room) =>
|
||||||
searchMode &&
|
searchMode &&
|
||||||
!room.displayname.toLowerCase().contains(
|
!room.displayname.toLowerCase().contains(
|
||||||
searchController.text.toLowerCase() ??
|
searchController.text.toLowerCase() ??
|
||||||
""));
|
''));
|
||||||
if (rooms.isEmpty &&
|
if (rooms.isEmpty &&
|
||||||
(!searchMode ||
|
(!searchMode ||
|
||||||
publicRoomsResponse == null)) {
|
publicRoomsResponse == null)) {
|
||||||
|
@ -410,10 +410,10 @@ class _ChatListState extends State<ChatList> {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
final int publicRoomsCount =
|
final publicRoomsCount =
|
||||||
(publicRoomsResponse?.publicRooms?.length ??
|
(publicRoomsResponse?.publicRooms?.length ??
|
||||||
0);
|
0);
|
||||||
final int totalCount =
|
final totalCount =
|
||||||
rooms.length + publicRoomsCount;
|
rooms.length + publicRoomsCount;
|
||||||
return ListView.separated(
|
return ListView.separated(
|
||||||
controller: _scrollController,
|
controller: _scrollController,
|
||||||
|
|
|
@ -8,7 +8,7 @@ import 'package:fluffychat/views/sign_up.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class HomeserverPicker extends StatelessWidget {
|
class HomeserverPicker extends StatelessWidget {
|
||||||
_setHomeserverAction(BuildContext context) async {
|
Future<void> _setHomeserverAction(BuildContext context) async {
|
||||||
final homeserver = await SimpleDialogs(context).enterText(
|
final homeserver = await SimpleDialogs(context).enterText(
|
||||||
titleText: L10n.of(context).enterYourHomeserver,
|
titleText: L10n.of(context).enterYourHomeserver,
|
||||||
hintText: Matrix.defaultHomeserver,
|
hintText: Matrix.defaultHomeserver,
|
||||||
|
@ -17,7 +17,7 @@ class HomeserverPicker extends StatelessWidget {
|
||||||
_checkHomeserverAction(homeserver, context);
|
_checkHomeserverAction(homeserver, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
_checkHomeserverAction(String homeserver, BuildContext context) async {
|
void _checkHomeserverAction(String homeserver, BuildContext context) async {
|
||||||
if (!homeserver.startsWith('https://')) {
|
if (!homeserver.startsWith('https://')) {
|
||||||
homeserver = 'https://$homeserver';
|
homeserver = 'https://$homeserver';
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,7 @@ class HomeserverPicker extends StatelessWidget {
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Hero(
|
Hero(
|
||||||
tag: 'loginBanner',
|
tag: 'loginBanner',
|
||||||
child: Image.asset("assets/fluffychat-banner.png"),
|
child: Image.asset('assets/fluffychat-banner.png'),
|
||||||
),
|
),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(16.0),
|
padding: const EdgeInsets.all(16.0),
|
||||||
|
|
|
@ -27,17 +27,18 @@ class _InvitationSelectionState extends State<InvitationSelection> {
|
||||||
Timer coolDown;
|
Timer coolDown;
|
||||||
|
|
||||||
Future<List<User>> getContacts(BuildContext context) async {
|
Future<List<User>> getContacts(BuildContext context) async {
|
||||||
final Client client = Matrix.of(context).client;
|
var client2 = Matrix.of(context).client;
|
||||||
List<User> participants = await widget.room.requestParticipants();
|
final client = client2;
|
||||||
|
var participants = await widget.room.requestParticipants();
|
||||||
participants.removeWhere(
|
participants.removeWhere(
|
||||||
(u) => ![Membership.join, Membership.invite].contains(u.membership),
|
(u) => ![Membership.join, Membership.invite].contains(u.membership),
|
||||||
);
|
);
|
||||||
List<User> contacts = [];
|
var contacts = <User>[];
|
||||||
Map<String, bool> userMap = {};
|
var userMap = <String, bool>{};
|
||||||
for (int i = 0; i < client.rooms.length; i++) {
|
for (var i = 0; i < client.rooms.length; i++) {
|
||||||
List<User> roomUsers = client.rooms[i].getParticipants();
|
var roomUsers = client.rooms[i].getParticipants();
|
||||||
|
|
||||||
for (int j = 0; j < roomUsers.length; j++) {
|
for (var j = 0; j < roomUsers.length; j++) {
|
||||||
if (userMap[roomUsers[j].id] != true &&
|
if (userMap[roomUsers[j].id] != true &&
|
||||||
participants.indexWhere((u) => u.id == roomUsers[j].id) == -1) {
|
participants.indexWhere((u) => u.id == roomUsers[j].id) == -1) {
|
||||||
contacts.add(roomUsers[j]);
|
contacts.add(roomUsers[j]);
|
||||||
|
@ -81,41 +82,41 @@ class _InvitationSelectionState extends State<InvitationSelection> {
|
||||||
if (currentSearchTerm.isEmpty) return;
|
if (currentSearchTerm.isEmpty) return;
|
||||||
if (loading) return;
|
if (loading) return;
|
||||||
setState(() => loading = true);
|
setState(() => loading = true);
|
||||||
final MatrixState matrix = Matrix.of(context);
|
final matrix = Matrix.of(context);
|
||||||
final response = await SimpleDialogs(context).tryRequestWithErrorToast(
|
final response = await SimpleDialogs(context).tryRequestWithErrorToast(
|
||||||
matrix.client.jsonRequest(
|
matrix.client.jsonRequest(
|
||||||
type: HTTPType.POST,
|
type: HTTPType.POST,
|
||||||
action: "/client/r0/user_directory/search",
|
action: '/client/r0/user_directory/search',
|
||||||
data: {
|
data: {
|
||||||
"search_term": text,
|
'search_term': text,
|
||||||
"limit": 10,
|
'limit': 10,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
setState(() => loading = false);
|
setState(() => loading = false);
|
||||||
if (response == false ||
|
if (response == false ||
|
||||||
!(response is Map) ||
|
!(response is Map) ||
|
||||||
(response["results"] == null)) return;
|
(response['results'] == null)) return;
|
||||||
setState(() {
|
setState(() {
|
||||||
foundProfiles = List<Map<String, dynamic>>.from(response["results"]);
|
foundProfiles = List<Map<String, dynamic>>.from(response['results']);
|
||||||
if ("@$text".isValidMatrixId &&
|
if ('@$text'.isValidMatrixId &&
|
||||||
foundProfiles
|
foundProfiles
|
||||||
.indexWhere((profile) => "@$text" == profile["user_id"]) ==
|
.indexWhere((profile) => '@$text' == profile['user_id']) ==
|
||||||
-1) {
|
-1) {
|
||||||
setState(() => foundProfiles = [
|
setState(() => foundProfiles = [
|
||||||
{"user_id": "@$text"}
|
{'user_id': '@$text'}
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
foundProfiles.removeWhere((profile) =>
|
foundProfiles.removeWhere((profile) =>
|
||||||
widget.room
|
widget.room
|
||||||
.getParticipants()
|
.getParticipants()
|
||||||
.indexWhere((u) => u.id == profile["user_id"]) !=
|
.indexWhere((u) => u.id == profile['user_id']) !=
|
||||||
-1);
|
-1);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final String groupName = widget.room.name?.isEmpty ?? false
|
final groupName = widget.room.name?.isEmpty ?? false
|
||||||
? L10n.of(context).group
|
? L10n.of(context).group
|
||||||
: widget.room.name;
|
: widget.room.name;
|
||||||
return AdaptivePageLayout(
|
return AdaptivePageLayout(
|
||||||
|
@ -138,7 +139,7 @@ class _InvitationSelectionState extends State<InvitationSelection> {
|
||||||
onSubmitted: (String text) => searchUser(context, text),
|
onSubmitted: (String text) => searchUser(context, text),
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
border: OutlineInputBorder(),
|
border: OutlineInputBorder(),
|
||||||
prefixText: "@",
|
prefixText: '@',
|
||||||
hintText: L10n.of(context).username,
|
hintText: L10n.of(context).username,
|
||||||
labelText: L10n.of(context).inviteContactToGroup(groupName),
|
labelText: L10n.of(context).inviteContactToGroup(groupName),
|
||||||
suffixIcon: loading
|
suffixIcon: loading
|
||||||
|
@ -159,19 +160,19 @@ class _InvitationSelectionState extends State<InvitationSelection> {
|
||||||
itemCount: foundProfiles.length,
|
itemCount: foundProfiles.length,
|
||||||
itemBuilder: (BuildContext context, int i) => ListTile(
|
itemBuilder: (BuildContext context, int i) => ListTile(
|
||||||
leading: Avatar(
|
leading: Avatar(
|
||||||
foundProfiles[i]["avatar_url"] == null
|
foundProfiles[i]['avatar_url'] == null
|
||||||
? null
|
? null
|
||||||
: Uri.parse(foundProfiles[i]["avatar_url"]),
|
: Uri.parse(foundProfiles[i]['avatar_url']),
|
||||||
foundProfiles[i]["display_name"] ??
|
foundProfiles[i]['display_name'] ??
|
||||||
foundProfiles[i]["user_id"],
|
foundProfiles[i]['user_id'],
|
||||||
),
|
),
|
||||||
title: Text(
|
title: Text(
|
||||||
foundProfiles[i]["display_name"] ??
|
foundProfiles[i]['display_name'] ??
|
||||||
(foundProfiles[i]["user_id"] as String).localpart,
|
(foundProfiles[i]['user_id'] as String).localpart,
|
||||||
),
|
),
|
||||||
subtitle: Text(foundProfiles[i]["user_id"]),
|
subtitle: Text(foundProfiles[i]['user_id']),
|
||||||
onTap: () =>
|
onTap: () =>
|
||||||
inviteAction(context, foundProfiles[i]["user_id"]),
|
inviteAction(context, foundProfiles[i]['user_id']),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
: FutureBuilder<List<User>>(
|
: FutureBuilder<List<User>>(
|
||||||
|
@ -182,7 +183,7 @@ class _InvitationSelectionState extends State<InvitationSelection> {
|
||||||
child: CircularProgressIndicator(),
|
child: CircularProgressIndicator(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
List<User> contacts = snapshot.data;
|
var contacts = snapshot.data;
|
||||||
return ListView.builder(
|
return ListView.builder(
|
||||||
itemCount: contacts.length,
|
itemCount: contacts.length,
|
||||||
itemBuilder: (BuildContext context, int i) => ListTile(
|
itemBuilder: (BuildContext context, int i) => ListTile(
|
||||||
|
|
|
@ -24,7 +24,7 @@ class _LoginState extends State<Login> {
|
||||||
bool showPassword = false;
|
bool showPassword = false;
|
||||||
|
|
||||||
void login(BuildContext context) async {
|
void login(BuildContext context) async {
|
||||||
MatrixState matrix = Matrix.of(context);
|
var matrix = Matrix.of(context);
|
||||||
if (usernameController.text.isEmpty) {
|
if (usernameController.text.isEmpty) {
|
||||||
setState(() => usernameError = L10n.of(context).pleaseEnterYourUsername);
|
setState(() => usernameError = L10n.of(context).pleaseEnterYourUsername);
|
||||||
} else {
|
} else {
|
||||||
|
@ -101,7 +101,7 @@ class _LoginState extends State<Login> {
|
||||||
controller: usernameController,
|
controller: usernameController,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
hintText:
|
hintText:
|
||||||
"@${L10n.of(context).username.toLowerCase()}:domain",
|
'@${L10n.of(context).username.toLowerCase()}:domain',
|
||||||
errorText: usernameError,
|
errorText: usernameError,
|
||||||
labelText: L10n.of(context).username),
|
labelText: L10n.of(context).username),
|
||||||
),
|
),
|
||||||
|
@ -120,7 +120,7 @@ class _LoginState extends State<Login> {
|
||||||
obscureText: !showPassword,
|
obscureText: !showPassword,
|
||||||
onSubmitted: (t) => login(context),
|
onSubmitted: (t) => login(context),
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
hintText: "****",
|
hintText: '****',
|
||||||
errorText: passwordError,
|
errorText: passwordError,
|
||||||
suffixIcon: IconButton(
|
suffixIcon: IconButton(
|
||||||
icon: Icon(showPassword
|
icon: Icon(showPassword
|
||||||
|
|
|
@ -31,18 +31,18 @@ class _NewGroupState extends State<_NewGroup> {
|
||||||
bool publicGroup = false;
|
bool publicGroup = false;
|
||||||
|
|
||||||
void submitAction(BuildContext context) async {
|
void submitAction(BuildContext context) async {
|
||||||
final MatrixState matrix = Matrix.of(context);
|
final matrix = Matrix.of(context);
|
||||||
Map<String, dynamic> params = {};
|
var params = <String, dynamic>{};
|
||||||
if (publicGroup) {
|
if (publicGroup) {
|
||||||
params["preset"] = "public_chat";
|
params['preset'] = 'public_chat';
|
||||||
params["visibility"] = "public";
|
params['visibility'] = 'public';
|
||||||
if (controller.text.isNotEmpty) {
|
if (controller.text.isNotEmpty) {
|
||||||
params["room_alias_name"] = controller.text;
|
params['room_alias_name'] = controller.text;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
params["preset"] = "private_chat";
|
params['preset'] = 'private_chat';
|
||||||
}
|
}
|
||||||
if (controller.text.isNotEmpty) params["name"] = controller.text;
|
if (controller.text.isNotEmpty) params['name'] = controller.text;
|
||||||
final String roomID =
|
final String roomID =
|
||||||
await SimpleDialogs(context).tryRequestWithLoadingDialog(
|
await SimpleDialogs(context).tryRequestWithLoadingDialog(
|
||||||
matrix.client.createRoom(params: params),
|
matrix.client.createRoom(params: params),
|
||||||
|
@ -99,7 +99,7 @@ class _NewGroupState extends State<_NewGroup> {
|
||||||
onChanged: (bool b) => setState(() => publicGroup = b),
|
onChanged: (bool b) => setState(() => publicGroup = b),
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Image.asset("assets/new_group_wallpaper.png"),
|
child: Image.asset('assets/new_group_wallpaper.png'),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
|
@ -37,23 +37,23 @@ class _NewPrivateChatState extends State<_NewPrivateChat> {
|
||||||
List<Map<String, dynamic>> foundProfiles = [];
|
List<Map<String, dynamic>> foundProfiles = [];
|
||||||
Timer coolDown;
|
Timer coolDown;
|
||||||
Map<String, dynamic> get foundProfile => foundProfiles.firstWhere(
|
Map<String, dynamic> get foundProfile => foundProfiles.firstWhere(
|
||||||
(user) => user["user_id"] == "@$currentSearchTerm",
|
(user) => user['user_id'] == '@$currentSearchTerm',
|
||||||
orElse: () => null);
|
orElse: () => null);
|
||||||
bool get correctMxId =>
|
bool get correctMxId =>
|
||||||
foundProfiles
|
foundProfiles
|
||||||
.indexWhere((user) => user["user_id"] == "@$currentSearchTerm") !=
|
.indexWhere((user) => user['user_id'] == '@$currentSearchTerm') !=
|
||||||
-1;
|
-1;
|
||||||
|
|
||||||
void submitAction(BuildContext context) async {
|
void submitAction(BuildContext context) async {
|
||||||
if (controller.text.isEmpty) return;
|
if (controller.text.isEmpty) return;
|
||||||
if (!_formKey.currentState.validate()) return;
|
if (!_formKey.currentState.validate()) return;
|
||||||
final MatrixState matrix = Matrix.of(context);
|
final matrix = Matrix.of(context);
|
||||||
|
|
||||||
if ("@" + controller.text.trim() == matrix.client.userID) return;
|
if ('@' + controller.text.trim() == matrix.client.userID) return;
|
||||||
|
|
||||||
final User user = User(
|
final user = User(
|
||||||
"@" + controller.text.trim(),
|
'@' + controller.text.trim(),
|
||||||
room: Room(id: "", client: matrix.client),
|
room: Room(id: '', client: matrix.client),
|
||||||
);
|
);
|
||||||
final String roomID = await SimpleDialogs(context)
|
final String roomID = await SimpleDialogs(context)
|
||||||
.tryRequestWithLoadingDialog(user.startDirectChat());
|
.tryRequestWithLoadingDialog(user.startDirectChat());
|
||||||
|
@ -87,22 +87,22 @@ class _NewPrivateChatState extends State<_NewPrivateChat> {
|
||||||
if (currentSearchTerm.isEmpty) return;
|
if (currentSearchTerm.isEmpty) return;
|
||||||
if (loading) return;
|
if (loading) return;
|
||||||
setState(() => loading = true);
|
setState(() => loading = true);
|
||||||
final MatrixState matrix = Matrix.of(context);
|
final matrix = Matrix.of(context);
|
||||||
final response = await SimpleDialogs(context).tryRequestWithErrorToast(
|
final response = await SimpleDialogs(context).tryRequestWithErrorToast(
|
||||||
matrix.client.jsonRequest(
|
matrix.client.jsonRequest(
|
||||||
type: HTTPType.POST,
|
type: HTTPType.POST,
|
||||||
action: "/client/r0/user_directory/search",
|
action: '/client/r0/user_directory/search',
|
||||||
data: {
|
data: {
|
||||||
"search_term": text,
|
'search_term': text,
|
||||||
"limit": 10,
|
'limit': 10,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
setState(() => loading = false);
|
setState(() => loading = false);
|
||||||
if (response == false ||
|
if (response == false ||
|
||||||
!(response is Map) ||
|
!(response is Map) ||
|
||||||
(response["results"]?.isEmpty ?? true)) return;
|
(response['results']?.isEmpty ?? true)) return;
|
||||||
setState(() {
|
setState(() {
|
||||||
foundProfiles = List<Map<String, dynamic>>.from(response["results"]);
|
foundProfiles = List<Map<String, dynamic>>.from(response['results']);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,15 +131,15 @@ class _NewPrivateChatState extends State<_NewPrivateChat> {
|
||||||
if (value.isEmpty) {
|
if (value.isEmpty) {
|
||||||
return L10n.of(context).pleaseEnterAMatrixIdentifier;
|
return L10n.of(context).pleaseEnterAMatrixIdentifier;
|
||||||
}
|
}
|
||||||
final MatrixState matrix = Matrix.of(context);
|
final matrix = Matrix.of(context);
|
||||||
String mxid = "@" + controller.text.trim();
|
var mxid = '@' + controller.text.trim();
|
||||||
if (mxid == matrix.client.userID) {
|
if (mxid == matrix.client.userID) {
|
||||||
return L10n.of(context).youCannotInviteYourself;
|
return L10n.of(context).youCannotInviteYourself;
|
||||||
}
|
}
|
||||||
if (!mxid.contains("@")) {
|
if (!mxid.contains('@')) {
|
||||||
return L10n.of(context).makeSureTheIdentifierIsValid;
|
return L10n.of(context).makeSureTheIdentifierIsValid;
|
||||||
}
|
}
|
||||||
if (!mxid.contains(":")) {
|
if (!mxid.contains(':')) {
|
||||||
return L10n.of(context).makeSureTheIdentifierIsValid;
|
return L10n.of(context).makeSureTheIdentifierIsValid;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
@ -158,17 +158,17 @@ class _NewPrivateChatState extends State<_NewPrivateChat> {
|
||||||
? Padding(
|
? Padding(
|
||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
child: Avatar(
|
child: Avatar(
|
||||||
foundProfile["avatar_url"] == null
|
foundProfile['avatar_url'] == null
|
||||||
? null
|
? null
|
||||||
: Uri.parse(foundProfile["avatar_url"]),
|
: Uri.parse(foundProfile['avatar_url']),
|
||||||
foundProfile["display_name"] ??
|
foundProfile['display_name'] ??
|
||||||
foundProfile["user_id"],
|
foundProfile['user_id'],
|
||||||
size: 12,
|
size: 12,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
: Icon(Icons.account_circle),
|
: Icon(Icons.account_circle),
|
||||||
prefixText: "@",
|
prefixText: '@',
|
||||||
hintText: "${L10n.of(context).username.toLowerCase()}",
|
hintText: '${L10n.of(context).username.toLowerCase()}',
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -179,29 +179,29 @@ class _NewPrivateChatState extends State<_NewPrivateChat> {
|
||||||
child: ListView.builder(
|
child: ListView.builder(
|
||||||
itemCount: foundProfiles.length,
|
itemCount: foundProfiles.length,
|
||||||
itemBuilder: (BuildContext context, int i) {
|
itemBuilder: (BuildContext context, int i) {
|
||||||
Map<String, dynamic> foundProfile = foundProfiles[i];
|
var foundProfile = foundProfiles[i];
|
||||||
return ListTile(
|
return ListTile(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
setState(() {
|
setState(() {
|
||||||
controller.text = currentSearchTerm =
|
controller.text = currentSearchTerm =
|
||||||
foundProfile["user_id"].substring(1);
|
foundProfile['user_id'].substring(1);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
leading: Avatar(
|
leading: Avatar(
|
||||||
foundProfile["avatar_url"] == null
|
foundProfile['avatar_url'] == null
|
||||||
? null
|
? null
|
||||||
: Uri.parse(foundProfile["avatar_url"]),
|
: Uri.parse(foundProfile['avatar_url']),
|
||||||
foundProfile["display_name"] ?? foundProfile["user_id"],
|
foundProfile['display_name'] ?? foundProfile['user_id'],
|
||||||
//size: 24,
|
//size: 24,
|
||||||
),
|
),
|
||||||
title: Text(
|
title: Text(
|
||||||
foundProfile["display_name"] ??
|
foundProfile['display_name'] ??
|
||||||
(foundProfile["user_id"] as String).localpart,
|
(foundProfile['user_id'] as String).localpart,
|
||||||
style: TextStyle(),
|
style: TextStyle(),
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
),
|
),
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
foundProfile["user_id"],
|
foundProfile['user_id'],
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
|
@ -219,9 +219,9 @@ class _NewPrivateChatState extends State<_NewPrivateChat> {
|
||||||
),
|
),
|
||||||
onTap: () => Share.share(L10n.of(context).inviteText(
|
onTap: () => Share.share(L10n.of(context).inviteText(
|
||||||
Matrix.of(context).client.userID,
|
Matrix.of(context).client.userID,
|
||||||
"https://matrix.to/#/${Matrix.of(context).client.userID}")),
|
'https://matrix.to/#/${Matrix.of(context).client.userID}')),
|
||||||
title: Text(
|
title: Text(
|
||||||
"${L10n.of(context).yourOwnUsername}:",
|
'${L10n.of(context).yourOwnUsername}:',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontStyle: FontStyle.italic,
|
fontStyle: FontStyle.italic,
|
||||||
),
|
),
|
||||||
|
@ -237,7 +237,7 @@ class _NewPrivateChatState extends State<_NewPrivateChat> {
|
||||||
Divider(height: 1),
|
Divider(height: 1),
|
||||||
if (foundProfiles.isEmpty || correctMxId)
|
if (foundProfiles.isEmpty || correctMxId)
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Image.asset("assets/private_chat_wallpaper.png"),
|
child: Image.asset('assets/private_chat_wallpaper.png'),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
import 'dart:io';
|
|
||||||
|
|
||||||
import 'package:famedlysdk/famedlysdk.dart';
|
import 'package:famedlysdk/famedlysdk.dart';
|
||||||
import 'package:fluffychat/components/settings_themes.dart';
|
import 'package:fluffychat/components/settings_themes.dart';
|
||||||
import 'package:fluffychat/views/settings_devices.dart';
|
import 'package:fluffychat/views/settings_devices.dart';
|
||||||
|
@ -42,7 +40,7 @@ class _SettingsState extends State<Settings> {
|
||||||
if (await SimpleDialogs(context).askConfirmation() == false) {
|
if (await SimpleDialogs(context).askConfirmation() == false) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
MatrixState matrix = Matrix.of(context);
|
var matrix = Matrix.of(context);
|
||||||
await SimpleDialogs(context)
|
await SimpleDialogs(context)
|
||||||
.tryRequestWithLoadingDialog(matrix.client.logout());
|
.tryRequestWithLoadingDialog(matrix.client.logout());
|
||||||
}
|
}
|
||||||
|
@ -57,20 +55,20 @@ class _SettingsState extends State<Settings> {
|
||||||
if (!jitsi.endsWith('/')) {
|
if (!jitsi.endsWith('/')) {
|
||||||
jitsi += '/';
|
jitsi += '/';
|
||||||
}
|
}
|
||||||
final MatrixState matrix = Matrix.of(context);
|
final matrix = Matrix.of(context);
|
||||||
await matrix.client.storeAPI.setItem('chat.fluffy.jitsi_instance', jitsi);
|
await matrix.store.setItem('chat.fluffy.jitsi_instance', jitsi);
|
||||||
matrix.jitsiInstance = jitsi;
|
matrix.jitsiInstance = jitsi;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setDisplaynameAction(BuildContext context) async {
|
void setDisplaynameAction(BuildContext context) async {
|
||||||
final String displayname = await SimpleDialogs(context).enterText(
|
final displayname = await SimpleDialogs(context).enterText(
|
||||||
titleText: L10n.of(context).editDisplayname,
|
titleText: L10n.of(context).editDisplayname,
|
||||||
hintText:
|
hintText:
|
||||||
profile?.displayname ?? Matrix.of(context).client.userID.localpart,
|
profile?.displayname ?? Matrix.of(context).client.userID.localpart,
|
||||||
labelText: L10n.of(context).enterAUsername,
|
labelText: L10n.of(context).enterAUsername,
|
||||||
);
|
);
|
||||||
if (displayname == null) return;
|
if (displayname == null) return;
|
||||||
final MatrixState matrix = Matrix.of(context);
|
final matrix = Matrix.of(context);
|
||||||
final success = await SimpleDialogs(context).tryRequestWithLoadingDialog(
|
final success = await SimpleDialogs(context).tryRequestWithLoadingDialog(
|
||||||
matrix.client.setDisplayname(displayname),
|
matrix.client.setDisplayname(displayname),
|
||||||
);
|
);
|
||||||
|
@ -83,13 +81,13 @@ class _SettingsState extends State<Settings> {
|
||||||
}
|
}
|
||||||
|
|
||||||
void setAvatarAction(BuildContext context) async {
|
void setAvatarAction(BuildContext context) async {
|
||||||
final File tempFile = await ImagePicker.pickImage(
|
final tempFile = await ImagePicker.pickImage(
|
||||||
source: ImageSource.gallery,
|
source: ImageSource.gallery,
|
||||||
imageQuality: 50,
|
imageQuality: 50,
|
||||||
maxWidth: 1600,
|
maxWidth: 1600,
|
||||||
maxHeight: 1600);
|
maxHeight: 1600);
|
||||||
if (tempFile == null) return;
|
if (tempFile == null) return;
|
||||||
final MatrixState matrix = Matrix.of(context);
|
final matrix = Matrix.of(context);
|
||||||
final success = await SimpleDialogs(context).tryRequestWithLoadingDialog(
|
final success = await SimpleDialogs(context).tryRequestWithLoadingDialog(
|
||||||
matrix.client.setAvatar(
|
matrix.client.setAvatar(
|
||||||
MatrixFile(
|
MatrixFile(
|
||||||
|
@ -111,24 +109,20 @@ class _SettingsState extends State<Settings> {
|
||||||
if (wallpaper == null) return;
|
if (wallpaper == null) return;
|
||||||
Matrix.of(context).wallpaper = wallpaper;
|
Matrix.of(context).wallpaper = wallpaper;
|
||||||
await Matrix.of(context)
|
await Matrix.of(context)
|
||||||
.client
|
.store
|
||||||
.storeAPI
|
.setItem('chat.fluffy.wallpaper', wallpaper.path);
|
||||||
.setItem("chat.fluffy.wallpaper", wallpaper.path);
|
|
||||||
setState(() => null);
|
setState(() => null);
|
||||||
}
|
}
|
||||||
|
|
||||||
void deleteWallpaperAction(BuildContext context) async {
|
void deleteWallpaperAction(BuildContext context) async {
|
||||||
Matrix.of(context).wallpaper = null;
|
Matrix.of(context).wallpaper = null;
|
||||||
await Matrix.of(context)
|
await Matrix.of(context).store.setItem('chat.fluffy.wallpaper', null);
|
||||||
.client
|
|
||||||
.storeAPI
|
|
||||||
.setItem("chat.fluffy.wallpaper", null);
|
|
||||||
setState(() => null);
|
setState(() => null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final Client client = Matrix.of(context).client;
|
final client = Matrix.of(context).client;
|
||||||
profileFuture ??= client.ownProfile;
|
profileFuture ??= client.ownProfile;
|
||||||
profileFuture.then((p) {
|
profileFuture.then((p) {
|
||||||
if (mounted) setState(() => profile = p);
|
if (mounted) setState(() => profile = p);
|
||||||
|
@ -174,8 +168,9 @@ class _SettingsState extends State<Settings> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
ThemesSettings(),
|
ThemesSettings(),
|
||||||
if (!kIsWeb && client.storeAPI != null) Divider(thickness: 1),
|
if (!kIsWeb && Matrix.of(context).store != null)
|
||||||
if (!kIsWeb && client.storeAPI != null)
|
Divider(thickness: 1),
|
||||||
|
if (!kIsWeb && Matrix.of(context).store != null)
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text(
|
title: Text(
|
||||||
L10n.of(context).wallpaper,
|
L10n.of(context).wallpaper,
|
||||||
|
@ -198,7 +193,7 @@ class _SettingsState extends State<Settings> {
|
||||||
),
|
),
|
||||||
onTap: () => deleteWallpaperAction(context),
|
onTap: () => deleteWallpaperAction(context),
|
||||||
),
|
),
|
||||||
if (!kIsWeb && client.storeAPI != null)
|
if (!kIsWeb && Matrix.of(context).store != null)
|
||||||
Builder(builder: (context) {
|
Builder(builder: (context) {
|
||||||
return ListTile(
|
return ListTile(
|
||||||
title: Text(L10n.of(context).changeWallpaper),
|
title: Text(L10n.of(context).changeWallpaper),
|
||||||
|
@ -223,8 +218,9 @@ class _SettingsState extends State<Settings> {
|
||||||
activeColor: Theme.of(context).primaryColor,
|
activeColor: Theme.of(context).primaryColor,
|
||||||
onChanged: (bool newValue) async {
|
onChanged: (bool newValue) async {
|
||||||
Matrix.of(context).renderHtml = newValue;
|
Matrix.of(context).renderHtml = newValue;
|
||||||
await client.storeAPI
|
await Matrix.of(context)
|
||||||
.setItem("chat.fluffy.renderHtml", newValue ? "1" : "0");
|
.store
|
||||||
|
.setItem('chat.fluffy.renderHtml', newValue ? '1' : '0');
|
||||||
setState(() => null);
|
setState(() => null);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
@ -300,19 +296,19 @@ class _SettingsState extends State<Settings> {
|
||||||
trailing: Icon(Icons.help),
|
trailing: Icon(Icons.help),
|
||||||
title: Text(L10n.of(context).help),
|
title: Text(L10n.of(context).help),
|
||||||
onTap: () => launch(
|
onTap: () => launch(
|
||||||
"https://gitlab.com/ChristianPauly/fluffychat-flutter/issues"),
|
'https://gitlab.com/ChristianPauly/fluffychat-flutter/issues'),
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
trailing: Icon(Icons.link),
|
trailing: Icon(Icons.link),
|
||||||
title: Text(L10n.of(context).license),
|
title: Text(L10n.of(context).license),
|
||||||
onTap: () => launch(
|
onTap: () => launch(
|
||||||
"https://gitlab.com/ChristianPauly/fluffychat-flutter/raw/master/LICENSE"),
|
'https://gitlab.com/ChristianPauly/fluffychat-flutter/raw/master/LICENSE'),
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
trailing: Icon(Icons.code),
|
trailing: Icon(Icons.code),
|
||||||
title: Text(L10n.of(context).sourceCode),
|
title: Text(L10n.of(context).sourceCode),
|
||||||
onTap: () => launch(
|
onTap: () => launch(
|
||||||
"https://gitlab.com/ChristianPauly/fluffychat-flutter"),
|
'https://gitlab.com/ChristianPauly/fluffychat-flutter'),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
|
@ -37,18 +37,18 @@ class DevicesSettingsState extends State<DevicesSettings> {
|
||||||
void _removeDevicesAction(
|
void _removeDevicesAction(
|
||||||
BuildContext context, List<UserDevice> devices) async {
|
BuildContext context, List<UserDevice> devices) async {
|
||||||
if (await SimpleDialogs(context).askConfirmation() == false) return;
|
if (await SimpleDialogs(context).askConfirmation() == false) return;
|
||||||
MatrixState matrix = Matrix.of(context);
|
var matrix = Matrix.of(context);
|
||||||
List<String> deviceIds = [];
|
var deviceIds = <String>[];
|
||||||
for (UserDevice userDevice in devices) {
|
for (var userDevice in devices) {
|
||||||
deviceIds.add(userDevice.deviceId);
|
deviceIds.add(userDevice.deviceId);
|
||||||
}
|
}
|
||||||
final success = await SimpleDialogs(context)
|
final success = await SimpleDialogs(context)
|
||||||
.tryRequestWithLoadingDialog(matrix.client.deleteDevices(deviceIds),
|
.tryRequestWithLoadingDialog(matrix.client.deleteDevices(deviceIds),
|
||||||
onAdditionalAuth: (MatrixException exception) async {
|
onAdditionalAuth: (MatrixException exception) async {
|
||||||
final String password = await SimpleDialogs(context).enterText(
|
final password = await SimpleDialogs(context).enterText(
|
||||||
titleText: L10n.of(context).pleaseEnterYourPassword,
|
titleText: L10n.of(context).pleaseEnterYourPassword,
|
||||||
labelText: L10n.of(context).pleaseEnterYourPassword,
|
labelText: L10n.of(context).pleaseEnterYourPassword,
|
||||||
hintText: "******",
|
hintText: '******',
|
||||||
password: true);
|
password: true);
|
||||||
if (password == null) return;
|
if (password == null) return;
|
||||||
await matrix.client.deleteDevices(deviceIds,
|
await matrix.client.deleteDevices(deviceIds,
|
||||||
|
@ -83,9 +83,8 @@ class DevicesSettingsState extends State<DevicesSettings> {
|
||||||
}
|
}
|
||||||
Function isOwnDevice = (UserDevice userDevice) =>
|
Function isOwnDevice = (UserDevice userDevice) =>
|
||||||
userDevice.deviceId == Matrix.of(context).client.deviceID;
|
userDevice.deviceId == Matrix.of(context).client.deviceID;
|
||||||
final List<UserDevice> devices = List<UserDevice>.from(this.devices);
|
final devices = List<UserDevice>.from(this.devices);
|
||||||
UserDevice thisDevice =
|
var thisDevice = devices.firstWhere(isOwnDevice, orElse: () => null);
|
||||||
devices.firstWhere(isOwnDevice, orElse: () => null);
|
|
||||||
devices.removeWhere(isOwnDevice);
|
devices.removeWhere(isOwnDevice);
|
||||||
devices.sort((a, b) => b.lastSeenTs.compareTo(a.lastSeenTs));
|
devices.sort((a, b) => b.lastSeenTs.compareTo(a.lastSeenTs));
|
||||||
return Column(
|
return Column(
|
||||||
|
@ -145,13 +144,13 @@ class UserDeviceListItem extends StatelessWidget {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return PopupMenuButton(
|
return PopupMenuButton(
|
||||||
onSelected: (String action) {
|
onSelected: (String action) {
|
||||||
if (action == "remove" && this.remove != null) {
|
if (action == 'remove' && remove != null) {
|
||||||
remove(userDevice);
|
remove(userDevice);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
itemBuilder: (BuildContext context) => [
|
itemBuilder: (BuildContext context) => [
|
||||||
PopupMenuItem<String>(
|
PopupMenuItem<String>(
|
||||||
value: "remove",
|
value: 'remove',
|
||||||
child: Text(L10n.of(context).removeDevice,
|
child: Text(L10n.of(context).removeDevice,
|
||||||
style: TextStyle(color: Colors.red)),
|
style: TextStyle(color: Colors.red)),
|
||||||
),
|
),
|
||||||
|
@ -175,8 +174,8 @@ class UserDeviceListItem extends StatelessWidget {
|
||||||
subtitle: Column(
|
subtitle: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text("${L10n.of(context).id}: ${userDevice.deviceId}"),
|
Text('${L10n.of(context).id}: ${userDevice.deviceId}'),
|
||||||
Text("${L10n.of(context).lastSeenIp}: ${userDevice.lastSeenIp}"),
|
Text('${L10n.of(context).lastSeenIp}: ${userDevice.lastSeenIp}'),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
import 'dart:io';
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter_advanced_networkimage/provider.dart';
|
import 'package:flutter_advanced_networkimage/provider.dart';
|
||||||
|
@ -55,7 +53,7 @@ class _EmotesSettingsState extends State<EmotesSettings> {
|
||||||
if (readonly) {
|
if (readonly) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
debugPrint("Saving....");
|
debugPrint('Saving....');
|
||||||
final client = Matrix.of(context).client;
|
final client = Matrix.of(context).client;
|
||||||
// be sure to preserve any data not in "short"
|
// be sure to preserve any data not in "short"
|
||||||
Map<String, dynamic> content;
|
Map<String, dynamic> content;
|
||||||
|
@ -95,7 +93,7 @@ class _EmotesSettingsState extends State<EmotesSettings> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
Client client = Matrix.of(context).client;
|
var client = Matrix.of(context).client;
|
||||||
if (emotes == null) {
|
if (emotes == null) {
|
||||||
emotes = <_EmoteEntry>[];
|
emotes = <_EmoteEntry>[];
|
||||||
Map<String, dynamic> emoteSource;
|
Map<String, dynamic> emoteSource;
|
||||||
|
@ -173,7 +171,7 @@ class _EmotesSettingsState extends State<EmotesSettings> {
|
||||||
size: 32.0,
|
size: 32.0,
|
||||||
),
|
),
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
debugPrint("blah");
|
debugPrint('blah');
|
||||||
if (newEmoteController.text == null ||
|
if (newEmoteController.text == null ||
|
||||||
newEmoteController.text.isEmpty ||
|
newEmoteController.text.isEmpty ||
|
||||||
newMxcController.text == null ||
|
newMxcController.text == null ||
|
||||||
|
@ -374,7 +372,7 @@ class _EmoteImagePickerState extends State<_EmoteImagePicker> {
|
||||||
BotToast.showText(text: L10n.of(context).notSupportedInWeb);
|
BotToast.showText(text: L10n.of(context).notSupportedInWeb);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
File file = await ImagePicker.pickImage(
|
var file = await ImagePicker.pickImage(
|
||||||
source: ImageSource.gallery,
|
source: ImageSource.gallery,
|
||||||
imageQuality: 50,
|
imageQuality: 50,
|
||||||
maxWidth: 128,
|
maxWidth: 128,
|
||||||
|
|
|
@ -23,7 +23,7 @@ class _SignUpState extends State<SignUp> {
|
||||||
File avatar;
|
File avatar;
|
||||||
|
|
||||||
void setAvatarAction() async {
|
void setAvatarAction() async {
|
||||||
File file = await ImagePicker.pickImage(
|
var file = await ImagePicker.pickImage(
|
||||||
source: ImageSource.gallery,
|
source: ImageSource.gallery,
|
||||||
maxHeight: 512,
|
maxHeight: 512,
|
||||||
maxWidth: 512,
|
maxWidth: 512,
|
||||||
|
@ -33,7 +33,7 @@ class _SignUpState extends State<SignUp> {
|
||||||
}
|
}
|
||||||
|
|
||||||
void signUpAction(BuildContext context) async {
|
void signUpAction(BuildContext context) async {
|
||||||
MatrixState matrix = Matrix.of(context);
|
var matrix = Matrix.of(context);
|
||||||
if (usernameController.text.isEmpty) {
|
if (usernameController.text.isEmpty) {
|
||||||
setState(() => usernameError = L10n.of(context).pleaseChooseAUsername);
|
setState(() => usernameError = L10n.of(context).pleaseChooseAUsername);
|
||||||
} else {
|
} else {
|
||||||
|
@ -45,8 +45,8 @@ class _SignUpState extends State<SignUp> {
|
||||||
}
|
}
|
||||||
setState(() => loading = true);
|
setState(() => loading = true);
|
||||||
|
|
||||||
final String preferredUsername =
|
final preferredUsername =
|
||||||
usernameController.text.toLowerCase().replaceAll(" ", "-");
|
usernameController.text.toLowerCase().replaceAll(' ', '-');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await matrix.client.usernameAvailable(preferredUsername);
|
await matrix.client.usernameAvailable(preferredUsername);
|
||||||
|
@ -83,7 +83,7 @@ class _SignUpState extends State<SignUp> {
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Hero(
|
Hero(
|
||||||
tag: 'loginBanner',
|
tag: 'loginBanner',
|
||||||
child: Image.asset("assets/fluffychat-banner.png"),
|
child: Image.asset('assets/fluffychat-banner.png'),
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: CircleAvatar(
|
leading: CircleAvatar(
|
||||||
|
|
|
@ -27,7 +27,7 @@ class _SignUpPasswordState extends State<SignUpPassword> {
|
||||||
bool showPassword = true;
|
bool showPassword = true;
|
||||||
|
|
||||||
void _signUpAction(BuildContext context, {Map<String, dynamic> auth}) async {
|
void _signUpAction(BuildContext context, {Map<String, dynamic> auth}) async {
|
||||||
MatrixState matrix = Matrix.of(context);
|
var matrix = Matrix.of(context);
|
||||||
if (passwordController.text.isEmpty) {
|
if (passwordController.text.isEmpty) {
|
||||||
setState(() => passwordError = L10n.of(context).pleaseEnterYourPassword);
|
setState(() => passwordError = L10n.of(context).pleaseEnterYourPassword);
|
||||||
} else {
|
} else {
|
||||||
|
@ -40,8 +40,7 @@ class _SignUpPasswordState extends State<SignUpPassword> {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
setState(() => loading = true);
|
setState(() => loading = true);
|
||||||
Future<LoginState> waitForLogin =
|
var waitForLogin = matrix.client.onLoginStateChanged.stream.first;
|
||||||
matrix.client.onLoginStateChanged.stream.first;
|
|
||||||
await matrix.client.register(
|
await matrix.client.register(
|
||||||
username: widget.username,
|
username: widget.username,
|
||||||
password: passwordController.text,
|
password: passwordController.text,
|
||||||
|
@ -51,21 +50,20 @@ class _SignUpPasswordState extends State<SignUpPassword> {
|
||||||
await waitForLogin;
|
await waitForLogin;
|
||||||
} on MatrixException catch (exception) {
|
} on MatrixException catch (exception) {
|
||||||
if (exception.requireAdditionalAuthentication) {
|
if (exception.requireAdditionalAuthentication) {
|
||||||
final List<String> stages = exception.authenticationFlows
|
final stages = exception.authenticationFlows
|
||||||
.firstWhere((a) => !a.stages.contains("m.login.email.identity"))
|
.firstWhere((a) => !a.stages.contains('m.login.email.identity'))
|
||||||
.stages;
|
.stages;
|
||||||
|
|
||||||
final String currentStage =
|
final currentStage = exception.completedAuthenticationFlows == null
|
||||||
exception.completedAuthenticationFlows == null
|
|
||||||
? stages.first
|
? stages.first
|
||||||
: stages.firstWhere((stage) =>
|
: stages.firstWhere((stage) =>
|
||||||
!exception.completedAuthenticationFlows.contains(stage) ??
|
!exception.completedAuthenticationFlows.contains(stage) ??
|
||||||
true);
|
true);
|
||||||
|
|
||||||
if (currentStage == "m.login.dummy") {
|
if (currentStage == 'm.login.dummy') {
|
||||||
_signUpAction(context, auth: {
|
_signUpAction(context, auth: {
|
||||||
"type": currentStage,
|
'type': currentStage,
|
||||||
"session": exception.session,
|
'session': exception.session,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
await Navigator.of(context).push(
|
await Navigator.of(context).push(
|
||||||
|
@ -75,7 +73,7 @@ class _SignUpPasswordState extends State<SignUpPassword> {
|
||||||
currentStage,
|
currentStage,
|
||||||
exception.session,
|
exception.session,
|
||||||
() => _signUpAction(context, auth: {
|
() => _signUpAction(context, auth: {
|
||||||
"session": exception.session,
|
'session': exception.session,
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -141,7 +139,7 @@ class _SignUpPasswordState extends State<SignUpPassword> {
|
||||||
autocorrect: false,
|
autocorrect: false,
|
||||||
onSubmitted: (t) => _signUpAction(context),
|
onSubmitted: (t) => _signUpAction(context),
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
hintText: "****",
|
hintText: '****',
|
||||||
errorText: passwordError,
|
errorText: passwordError,
|
||||||
suffixIcon: IconButton(
|
suffixIcon: IconButton(
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
|
|
154
pubspec.lock
154
pubspec.lock
|
@ -7,14 +7,14 @@ packages:
|
||||||
name: _fe_analyzer_shared
|
name: _fe_analyzer_shared
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.3"
|
version: "3.0.0"
|
||||||
analyzer:
|
analyzer:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: analyzer
|
name: analyzer
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.39.4"
|
version: "0.39.8"
|
||||||
archive:
|
archive:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -91,7 +91,7 @@ packages:
|
||||||
name: coverage
|
name: coverage
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.13.6"
|
version: "0.13.9"
|
||||||
crypto:
|
crypto:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -119,13 +119,22 @@ packages:
|
||||||
name: dart_style
|
name: dart_style
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.3"
|
version: "1.3.6"
|
||||||
|
encrypted_moor:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
path: "extras/encryption"
|
||||||
|
ref: HEAD
|
||||||
|
resolved-ref: "6f930b011577e5bc8a5e5511691c8fcc43869a1c"
|
||||||
|
url: "https://github.com/simolus3/moor.git"
|
||||||
|
source: git
|
||||||
|
version: "1.0.0"
|
||||||
famedlysdk:
|
famedlysdk:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
path: "."
|
path: "."
|
||||||
ref: "2525b3d9f156fa303ca9283a96fd8cf8db154dd9"
|
ref: "2455bac3bf8dab846ba453a6393f0be2c0b61001"
|
||||||
resolved-ref: "2525b3d9f156fa303ca9283a96fd8cf8db154dd9"
|
resolved-ref: "2455bac3bf8dab846ba453a6393f0be2c0b61001"
|
||||||
url: "https://gitlab.com/famedly/famedlysdk.git"
|
url: "https://gitlab.com/famedly/famedlysdk.git"
|
||||||
source: git
|
source: git
|
||||||
version: "0.0.1"
|
version: "0.0.1"
|
||||||
|
@ -142,14 +151,14 @@ packages:
|
||||||
name: file_picker
|
name: file_picker
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.4.3+2"
|
version: "1.9.0+1"
|
||||||
firebase_messaging:
|
firebase_messaging:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: firebase_messaging
|
name: firebase_messaging
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.0.13"
|
version: "6.0.15"
|
||||||
flutter:
|
flutter:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description: flutter
|
description: flutter
|
||||||
|
@ -161,14 +170,14 @@ packages:
|
||||||
name: flutter_advanced_networkimage
|
name: flutter_advanced_networkimage
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.6.4"
|
version: "0.7.0"
|
||||||
flutter_launcher_icons:
|
flutter_launcher_icons:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
name: flutter_launcher_icons
|
name: flutter_launcher_icons
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.7.4"
|
version: "0.7.5"
|
||||||
flutter_local_notifications:
|
flutter_local_notifications:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -195,13 +204,20 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.0.5"
|
version: "0.0.5"
|
||||||
|
flutter_plugin_android_lifecycle:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_plugin_android_lifecycle
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.7"
|
||||||
flutter_secure_storage:
|
flutter_secure_storage:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: flutter_secure_storage
|
name: flutter_secure_storage
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.3.1+1"
|
version: "3.3.3"
|
||||||
flutter_slidable:
|
flutter_slidable:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -281,7 +297,7 @@ packages:
|
||||||
name: http_parser
|
name: http_parser
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.3"
|
version: "3.1.4"
|
||||||
image:
|
image:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -295,7 +311,14 @@ packages:
|
||||||
name: image_picker
|
name: image_picker
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.6.2+3"
|
version: "0.6.6+1"
|
||||||
|
image_picker_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: image_picker_platform_interface
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.0"
|
||||||
intl:
|
intl:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -316,7 +339,7 @@ packages:
|
||||||
name: io
|
name: io
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.3.3"
|
version: "0.3.4"
|
||||||
js:
|
js:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -330,14 +353,14 @@ packages:
|
||||||
name: link_text
|
name: link_text
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.1.1"
|
version: "0.1.2"
|
||||||
localstorage:
|
localstorage:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: localstorage
|
name: localstorage
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.1+4"
|
version: "3.0.2+5"
|
||||||
logging:
|
logging:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -389,6 +412,13 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.3.0"
|
version: "0.3.0"
|
||||||
|
moor:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: moor
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.2"
|
||||||
multi_server_socket:
|
multi_server_socket:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -402,14 +432,14 @@ packages:
|
||||||
name: node_interop
|
name: node_interop
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.3"
|
version: "1.1.1"
|
||||||
node_io:
|
node_io:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: node_io
|
name: node_io
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.1+2"
|
version: "1.1.1"
|
||||||
node_preamble:
|
node_preamble:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -439,14 +469,7 @@ packages:
|
||||||
name: package_config
|
name: package_config
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.0"
|
version: "1.9.3"
|
||||||
package_resolver:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: package_resolver
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "1.0.10"
|
|
||||||
path:
|
path:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -474,14 +497,28 @@ packages:
|
||||||
name: path_provider
|
name: path_provider
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.5.1"
|
version: "1.6.8"
|
||||||
|
path_provider_macos:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_provider_macos
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.0.4+2"
|
||||||
|
path_provider_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_provider_platform_interface
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.2"
|
||||||
pedantic:
|
pedantic:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
name: pedantic
|
name: pedantic
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.8.0+1"
|
version: "1.9.0"
|
||||||
petitparser:
|
petitparser:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -509,7 +546,7 @@ packages:
|
||||||
name: plugin_platform_interface
|
name: plugin_platform_interface
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.1"
|
version: "1.0.2"
|
||||||
pointycastle:
|
pointycastle:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -530,7 +567,7 @@ packages:
|
||||||
name: pub_semver
|
name: pub_semver
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.4.2"
|
version: "1.4.4"
|
||||||
quiver:
|
quiver:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -538,20 +575,27 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.3"
|
version: "2.1.3"
|
||||||
|
random_string:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: random_string
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.1"
|
||||||
receive_sharing_intent:
|
receive_sharing_intent:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: receive_sharing_intent
|
name: receive_sharing_intent
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.3"
|
version: "1.4.0+2"
|
||||||
share:
|
share:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: share
|
name: share
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.6.3+5"
|
version: "0.6.4+2"
|
||||||
shelf:
|
shelf:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -565,7 +609,7 @@ packages:
|
||||||
name: shelf_packages_handler
|
name: shelf_packages_handler
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.4"
|
version: "2.0.0"
|
||||||
shelf_static:
|
shelf_static:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -591,7 +635,7 @@ packages:
|
||||||
name: source_map_stack_trace
|
name: source_map_stack_trace
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.5"
|
version: "2.0.0"
|
||||||
source_maps:
|
source_maps:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -612,7 +656,21 @@ packages:
|
||||||
name: sqflite
|
name: sqflite
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.0"
|
version: "1.3.0+1"
|
||||||
|
sqflite_common:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: sqflite_common
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.1"
|
||||||
|
sqflite_sqlcipher:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: sqflite_sqlcipher
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.0+6"
|
||||||
stack_trace:
|
stack_trace:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -640,7 +698,7 @@ packages:
|
||||||
name: synchronized
|
name: synchronized
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.1"
|
version: "2.2.0"
|
||||||
term_glyph:
|
term_glyph:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -654,7 +712,7 @@ packages:
|
||||||
name: test
|
name: test
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.13.0"
|
version: "1.14.3"
|
||||||
test_api:
|
test_api:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -668,7 +726,7 @@ packages:
|
||||||
name: test_core
|
name: test_core
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.3.1"
|
version: "0.3.4"
|
||||||
typed_data:
|
typed_data:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -682,14 +740,14 @@ packages:
|
||||||
name: universal_html
|
name: universal_html
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.12"
|
version: "1.2.2"
|
||||||
universal_io:
|
universal_io:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: universal_io
|
name: universal_io
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.8.6"
|
version: "1.0.1"
|
||||||
unorm_dart:
|
unorm_dart:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -703,28 +761,28 @@ packages:
|
||||||
name: url_launcher
|
name: url_launcher
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.4.1"
|
version: "5.4.7"
|
||||||
url_launcher_macos:
|
url_launcher_macos:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: url_launcher_macos
|
name: url_launcher_macos
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.0.1+2"
|
version: "0.0.1+5"
|
||||||
url_launcher_platform_interface:
|
url_launcher_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: url_launcher_platform_interface
|
name: url_launcher_platform_interface
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.5"
|
version: "1.0.7"
|
||||||
url_launcher_web:
|
url_launcher_web:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: url_launcher_web
|
name: url_launcher_web
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.1.0+2"
|
version: "0.1.1+5"
|
||||||
vector_math:
|
vector_math:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -738,14 +796,14 @@ packages:
|
||||||
name: vm_service
|
name: vm_service
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.1"
|
version: "4.0.4"
|
||||||
watcher:
|
watcher:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: watcher
|
name: watcher
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.9.7+13"
|
version: "0.9.7+15"
|
||||||
web_socket_channel:
|
web_socket_channel:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -766,7 +824,7 @@ packages:
|
||||||
name: webview_flutter
|
name: webview_flutter
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.3.19+9"
|
version: "0.3.21"
|
||||||
xml:
|
xml:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -780,7 +838,7 @@ packages:
|
||||||
name: yaml
|
name: yaml
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.0"
|
version: "2.2.1"
|
||||||
zone_local:
|
zone_local:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
11
pubspec.yaml
11
pubspec.yaml
|
@ -27,7 +27,7 @@ dependencies:
|
||||||
famedlysdk:
|
famedlysdk:
|
||||||
git:
|
git:
|
||||||
url: https://gitlab.com/famedly/famedlysdk.git
|
url: https://gitlab.com/famedly/famedlysdk.git
|
||||||
ref: 2525b3d9f156fa303ca9283a96fd8cf8db154dd9
|
ref: 2455bac3bf8dab846ba453a6393f0be2c0b61001
|
||||||
|
|
||||||
localstorage: ^3.0.1+4
|
localstorage: ^3.0.1+4
|
||||||
bubble: ^1.1.9+1
|
bubble: ^1.1.9+1
|
||||||
|
@ -55,18 +55,25 @@ dependencies:
|
||||||
mime_type: ^0.3.0
|
mime_type: ^0.3.0
|
||||||
bot_toast: ^3.0.0
|
bot_toast: ^3.0.0
|
||||||
flutter_matrix_html: ^0.0.5
|
flutter_matrix_html: ^0.0.5
|
||||||
|
moor: ^3.0.2
|
||||||
|
random_string: ^2.0.1
|
||||||
|
|
||||||
intl: ^0.16.0
|
intl: ^0.16.0
|
||||||
intl_translation: ^0.17.9
|
intl_translation: ^0.17.9
|
||||||
flutter_localizations:
|
flutter_localizations:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
|
|
||||||
|
encrypted_moor:
|
||||||
|
git:
|
||||||
|
url: https://github.com/simolus3/moor.git
|
||||||
|
path: extras/encryption
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
|
|
||||||
flutter_launcher_icons: "^0.7.4"
|
flutter_launcher_icons: "^0.7.4"
|
||||||
pedantic: ^1.5.0
|
pedantic: ^1.9.0
|
||||||
|
|
||||||
flutter_icons:
|
flutter_icons:
|
||||||
android: "launcher_icon"
|
android: "launcher_icon"
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<script src="assets/assets/js/package/olm.js"></script>
|
<script src="assets/assets/js/package/olm.js"></script>
|
||||||
|
<script defer src="sql-wasm.js"></script>
|
||||||
<script src="main.dart.js" type="application/javascript"></script>
|
<script src="main.dart.js" type="application/javascript"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
209
web/sql-wasm.js
Normal file
209
web/sql-wasm.js
Normal file
|
@ -0,0 +1,209 @@
|
||||||
|
|
||||||
|
// We are modularizing this manually because the current modularize setting in Emscripten has some issues:
|
||||||
|
// https://github.com/kripken/emscripten/issues/5820
|
||||||
|
// In addition, When you use emcc's modularization, it still expects to export a global object called `Module`,
|
||||||
|
// which is able to be used/called before the WASM is loaded.
|
||||||
|
// The modularization below exports a promise that loads and resolves to the actual sql.js module.
|
||||||
|
// That way, this module can't be used before the WASM is finished loading.
|
||||||
|
|
||||||
|
// We are going to define a function that a user will call to start loading initializing our Sql.js library
|
||||||
|
// However, that function might be called multiple times, and on subsequent calls, we don't actually want it to instantiate a new instance of the Module
|
||||||
|
// Instead, we want to return the previously loaded module
|
||||||
|
|
||||||
|
// TODO: Make this not declare a global if used in the browser
|
||||||
|
var initSqlJsPromise = undefined;
|
||||||
|
|
||||||
|
var initSqlJs = function (moduleConfig) {
|
||||||
|
|
||||||
|
if (initSqlJsPromise){
|
||||||
|
return initSqlJsPromise;
|
||||||
|
}
|
||||||
|
// If we're here, we've never called this function before
|
||||||
|
initSqlJsPromise = new Promise((resolveModule, reject) => {
|
||||||
|
|
||||||
|
// We are modularizing this manually because the current modularize setting in Emscripten has some issues:
|
||||||
|
// https://github.com/kripken/emscripten/issues/5820
|
||||||
|
|
||||||
|
// The way to affect the loading of emcc compiled modules is to create a variable called `Module` and add
|
||||||
|
// properties to it, like `preRun`, `postRun`, etc
|
||||||
|
// We are using that to get notified when the WASM has finished loading.
|
||||||
|
// Only then will we return our promise
|
||||||
|
|
||||||
|
// If they passed in a moduleConfig object, use that
|
||||||
|
// Otherwise, initialize Module to the empty object
|
||||||
|
var Module = typeof moduleConfig !== 'undefined' ? moduleConfig : {};
|
||||||
|
|
||||||
|
// EMCC only allows for a single onAbort function (not an array of functions)
|
||||||
|
// So if the user defined their own onAbort function, we remember it and call it
|
||||||
|
var originalOnAbortFunction = Module['onAbort'];
|
||||||
|
Module['onAbort'] = function (errorThatCausedAbort) {
|
||||||
|
reject(new Error(errorThatCausedAbort));
|
||||||
|
if (originalOnAbortFunction){
|
||||||
|
originalOnAbortFunction(errorThatCausedAbort);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Module['postRun'] = Module['postRun'] || [];
|
||||||
|
Module['postRun'].push(function () {
|
||||||
|
// When Emscripted calls postRun, this promise resolves with the built Module
|
||||||
|
resolveModule(Module);
|
||||||
|
});
|
||||||
|
|
||||||
|
// There is a section of code in the emcc-generated code below that looks like this:
|
||||||
|
// (Note that this is lowercase `module`)
|
||||||
|
// if (typeof module !== 'undefined') {
|
||||||
|
// module['exports'] = Module;
|
||||||
|
// }
|
||||||
|
// When that runs, it's going to overwrite our own modularization export efforts in shell-post.js!
|
||||||
|
// The only way to tell emcc not to emit it is to pass the MODULARIZE=1 or MODULARIZE_INSTANCE=1 flags,
|
||||||
|
// but that carries with it additional unnecessary baggage/bugs we don't want either.
|
||||||
|
// So, we have three options:
|
||||||
|
// 1) We undefine `module`
|
||||||
|
// 2) We remember what `module['exports']` was at the beginning of this function and we restore it later
|
||||||
|
// 3) We write a script to remove those lines of code as part of the Make process.
|
||||||
|
//
|
||||||
|
// Since those are the only lines of code that care about module, we will undefine it. It's the most straightforward
|
||||||
|
// of the options, and has the side effect of reducing emcc's efforts to modify the module if its output were to change in the future.
|
||||||
|
// That's a nice side effect since we're handling the modularization efforts ourselves
|
||||||
|
module = undefined;
|
||||||
|
|
||||||
|
// The emcc-generated code and shell-post.js code goes below,
|
||||||
|
// meaning that all of it runs inside of this promise. If anything throws an exception, our promise will abort
|
||||||
|
var aa;var f;f||(f=typeof Module !== 'undefined' ? Module : {});
|
||||||
|
var va=function(){var a;var b=h(4);var c={};var d=function(){function a(a,b){this.fb=a;this.db=b;this.nb=1;this.Eb=[]}a.prototype.bind=function(a){if(!this.fb)throw"Statement closed";this.reset();return Array.isArray(a)?this.lc(a):this.mc(a)};a.prototype.step=function(){var a;if(!this.fb)throw"Statement closed";this.nb=1;switch(a=Tb(this.fb)){case c.hc:return!0;case c.DONE:return!1;default:return this.db.handleError(a)}};a.prototype.sc=function(a){null==a&&(a=this.nb++);return Ub(this.fb,a)};a.prototype.tc=
|
||||||
|
function(a){null==a&&(a=this.nb++);return Vb(this.fb,a)};a.prototype.getBlob=function(a){var b;null==a&&(a=this.nb++);var c=Wb(this.fb,a);var d=Xb(this.fb,a);var e=new Uint8Array(c);for(a=b=0;0<=c?b<c:b>c;a=0<=c?++b:--b)e[a]=l[d+a];return e};a.prototype.get=function(a){var b,d;null!=a&&this.bind(a)&&this.step();var e=[];a=b=0;for(d=ib(this.fb);0<=d?b<d:b>d;a=0<=d?++b:--b)switch(Yb(this.fb,a)){case c.fc:case c.FLOAT:e.push(this.sc(a));break;case c.ic:e.push(this.tc(a));break;case c.Zb:e.push(this.getBlob(a));
|
||||||
|
break;default:e.push(null)}return e};a.prototype.getColumnNames=function(){var a,b;var c=[];var d=a=0;for(b=ib(this.fb);0<=b?a<b:a>b;d=0<=b?++a:--a)c.push(Zb(this.fb,d));return c};a.prototype.getAsObject=function(a){var b,c;var d=this.get(a);var e=this.getColumnNames();var g={};a=b=0;for(c=e.length;b<c;a=++b){var Sb=e[a];g[Sb]=d[a]}return g};a.prototype.run=function(a){null!=a&&this.bind(a);this.step();return this.reset()};a.prototype.pc=function(a,b){var c;null==b&&(b=this.nb++);a=ba(a);this.Eb.push(c=
|
||||||
|
ea(a));this.db.handleError(ca(this.fb,b,c,a.length-1,0))};a.prototype.kc=function(a,b){var c;null==b&&(b=this.nb++);this.Eb.push(c=ea(a));this.db.handleError(Ia(this.fb,b,c,a.length,0))};a.prototype.oc=function(a,b){null==b&&(b=this.nb++);this.db.handleError((a===(a|0)?$b:ac)(this.fb,b,a))};a.prototype.nc=function(a){null==a&&(a=this.nb++);Ia(this.fb,a,0,0,0)};a.prototype.Qb=function(a,b){null==b&&(b=this.nb++);switch(typeof a){case "string":this.pc(a,b);break;case "number":case "boolean":this.oc(a+
|
||||||
|
0,b);break;case "object":if(null===a)this.nc(b);else if(null!=a.length)this.kc(a,b);else throw"Wrong API use : tried to bind a value of an unknown type ("+a+").";}};a.prototype.mc=function(a){var b;for(b in a){var c=a[b];var d=bc(this.fb,b);0!==d&&this.Qb(c,d)}return!0};a.prototype.lc=function(a){var b,c;var d=b=0;for(c=a.length;b<c;d=++b){var e=a[d];this.Qb(e,d+1)}return!0};a.prototype.reset=function(){this.freemem();return cc(this.fb)===c.xb&&dc(this.fb)===c.xb};a.prototype.freemem=function(){for(var a;a=
|
||||||
|
this.Eb.pop();)ha(a);return null};a.prototype.free=function(){this.freemem();var a=ec(this.fb)===c.xb;delete this.db.Bb[this.fb];this.fb=da;return a};return a}();var e=function(){function a(a){this.filename="dbfile_"+(4294967295*Math.random()>>>0);if(null!=a){var c=this.filename,d=c?n("/",c):"/";c=ia(!0,!0);d=ja(d,(void 0!==c?c:438)&4095|32768,0);if(a){if("string"===typeof a){for(var e=Array(a.length),k=0,m=a.length;k<m;++k)e[k]=a.charCodeAt(k);a=e}ka(d,c|146);e=p(d,"w");la(e,a,0,a.length,0,void 0);
|
||||||
|
ma(e);ka(d,c)}}this.handleError(g(this.filename,b));this.db=q(b,"i32");fc(this.db);this.Bb={}}a.prototype.run=function(a,c){if(!this.db)throw"Database closed";c?(a=this.prepare(a,c),a.step(),a.free()):this.handleError(m(this.db,a,0,0,b));return this};a.prototype.exec=function(a){if(!this.db)throw"Database closed";var c=na();var e=oa(a)+1;var g=h(e);r(a,l,g,e);a=g;e=h(4);for(g=[];q(a,"i8")!==da;){pa(b);pa(e);this.handleError(fa(this.db,a,-1,b,e));var k=q(b,"i32");a=q(e,"i32");if(k!==da){var m=new d(k,
|
||||||
|
this);for(k=null;m.step();)null===k&&(k={columns:m.getColumnNames(),values:[]},g.push(k)),k.values.push(m.get());m.free()}}qa(c);return g};a.prototype.each=function(a,b,c,d){"function"===typeof b&&(d=c,c=b,b=void 0);for(a=this.prepare(a,b);a.step();)c(a.getAsObject());a.free();if("function"===typeof d)return d()};a.prototype.prepare=function(a,c){pa(b);this.handleError(z(this.db,a,-1,b,da));a=q(b,"i32");if(a===da)throw"Nothing to prepare";var e=new d(a,this);null!=c&&e.bind(c);return this.Bb[a]=e};
|
||||||
|
a.prototype["export"]=function(){var a;var c=this.Bb;for(e in c){var d=c[e];d.free()}this.handleError(k(this.db));d=this.filename;var e=e={encoding:"binary"};e.flags=e.flags||"r";e.encoding=e.encoding||"binary";if("utf8"!==e.encoding&&"binary"!==e.encoding)throw Error('Invalid encoding type "'+e.encoding+'"');c=p(d,e.flags);d=ra(d).size;var m=new Uint8Array(d);sa(c,m,0,d,0);"utf8"===e.encoding?a=t(m,0):"binary"===e.encoding&&(a=m);ma(c);this.handleError(g(this.filename,b));this.db=q(b,"i32");return a};
|
||||||
|
a.prototype.close=function(){var a;var b=this.Bb;for(a in b){var c=b[a];c.free()}this.handleError(k(this.db));ta("/"+this.filename);return this.db=null};a.prototype.handleError=function(a){if(a===c.xb)return null;a=hc(this.db);throw Error(a);};a.prototype.getRowsModified=function(){return y(this.db)};a.prototype.create_function=function(a,b){var d=ua(function(a,c,d){var e,g;var k=[];for(e=g=0;0<=c?g<c:g>c;e=0<=c?++g:--g){var m=q(d+4*e,"i32");var z=jc(m);e=function(){switch(!1){case 1!==z:return kc;
|
||||||
|
case 2!==z:return lc;case 3!==z:return mc;case 4!==z:return function(a){var b,c;var d=nc(a);var e=oc(a);a=new Uint8Array(d);for(b=c=0;0<=d?c<d:c>d;b=0<=d?++c:--c)a[b]=l[e+b];return a};default:return function(){return null}}}();e=e(m);k.push(e)}if(c=b.apply(null,k))switch(typeof c){case "number":return pc(a,c);case "string":return qc(a,c,-1,-1)}else return rc(a)});this.handleError(sc(this.db,a,b.length,c.jc,0,d,0,0,0));return this};return a}();var g=f.cwrap("sqlite3_open","number",["string","number"]);
|
||||||
|
var k=f.cwrap("sqlite3_close_v2","number",["number"]);var m=f.cwrap("sqlite3_exec","number",["number","string","number","number","number"]);f.cwrap("sqlite3_free","",["number"]);var y=f.cwrap("sqlite3_changes","number",["number"]);var z=f.cwrap("sqlite3_prepare_v2","number",["number","string","number","number","number"]);var fa=f.cwrap("sqlite3_prepare_v2","number",["number","number","number","number","number"]);var ca=f.cwrap("sqlite3_bind_text","number",["number","number","number","number","number"]);
|
||||||
|
var Ia=f.cwrap("sqlite3_bind_blob","number",["number","number","number","number","number"]);var ac=f.cwrap("sqlite3_bind_double","number",["number","number","number"]);var $b=f.cwrap("sqlite3_bind_int","number",["number","number","number"]);var bc=f.cwrap("sqlite3_bind_parameter_index","number",["number","string"]);var Tb=f.cwrap("sqlite3_step","number",["number"]);var hc=f.cwrap("sqlite3_errmsg","string",["number"]);var ib=f.cwrap("sqlite3_data_count","number",["number"]);var Ub=f.cwrap("sqlite3_column_double",
|
||||||
|
"number",["number","number"]);var Vb=f.cwrap("sqlite3_column_text","string",["number","number"]);var Xb=f.cwrap("sqlite3_column_blob","number",["number","number"]);var Wb=f.cwrap("sqlite3_column_bytes","number",["number","number"]);var Yb=f.cwrap("sqlite3_column_type","number",["number","number"]);var Zb=f.cwrap("sqlite3_column_name","string",["number","number"]);var dc=f.cwrap("sqlite3_reset","number",["number"]);var cc=f.cwrap("sqlite3_clear_bindings","number",["number"]);var ec=f.cwrap("sqlite3_finalize",
|
||||||
|
"number",["number"]);var sc=f.cwrap("sqlite3_create_function_v2","number","number string number number number number number number number".split(" "));var jc=f.cwrap("sqlite3_value_type","number",["number"]);var nc=f.cwrap("sqlite3_value_bytes","number",["number"]);var mc=f.cwrap("sqlite3_value_text","string",["number"]);var kc=f.cwrap("sqlite3_value_int","number",["number"]);var oc=f.cwrap("sqlite3_value_blob","number",["number"]);var lc=f.cwrap("sqlite3_value_double","number",["number"]);var pc=
|
||||||
|
f.cwrap("sqlite3_result_double","",["number","number"]);var rc=f.cwrap("sqlite3_result_null","",["number"]);var qc=f.cwrap("sqlite3_result_text","",["number","string","number","number"]);var fc=f.cwrap("RegisterExtensionFunctions","number",["number"]);this.SQL={Database:e};for(a in this.SQL)f[a]=this.SQL[a];var da=0;c.xb=0;c.we=1;c.Pe=2;c.Ze=3;c.Cc=4;c.Ec=5;c.Se=6;c.NOMEM=7;c.bf=8;c.Qe=9;c.Re=10;c.Hc=11;c.NOTFOUND=12;c.Oe=13;c.Fc=14;c.$e=15;c.EMPTY=16;c.cf=17;c.df=18;c.Gc=19;c.Te=20;c.Ue=21;c.Ve=
|
||||||
|
22;c.Dc=23;c.Ne=24;c.af=25;c.We=26;c.Xe=27;c.ef=28;c.hc=100;c.DONE=101;c.fc=1;c.FLOAT=2;c.ic=3;c.Zb=4;c.Ye=5;c.jc=1}.bind(this);f.preRun=f.preRun||[];f.preRun.push(va);var wa={},u;for(u in f)f.hasOwnProperty(u)&&(wa[u]=f[u]);f.arguments=[];f.thisProgram="./this.program";f.quit=function(a,b){throw b;};f.preRun=[];f.postRun=[];var v=!1,w=!1,x=!1,xa=!1;v="object"===typeof window;w="function"===typeof importScripts;x="object"===typeof process&&"function"===typeof require&&!v&&!w;xa=!v&&!x&&!w;var A="";
|
||||||
|
if(x){A=__dirname+"/";var ya,za;f.read=function(a,b){ya||(ya=require("fs"));za||(za=require("path"));a=za.normalize(a);a=ya.readFileSync(a);return b?a:a.toString()};f.readBinary=function(a){a=f.read(a,!0);a.buffer||(a=new Uint8Array(a));assert(a.buffer);return a};1<process.argv.length&&(f.thisProgram=process.argv[1].replace(/\\/g,"/"));f.arguments=process.argv.slice(2);"undefined"!==typeof module&&(module.exports=f);process.on("unhandledRejection",B);f.quit=function(a){process.exit(a)};f.inspect=
|
||||||
|
function(){return"[Emscripten Module object]"}}else if(xa)"undefined"!=typeof read&&(f.read=function(a){return read(a)}),f.readBinary=function(a){if("function"===typeof readbuffer)return new Uint8Array(readbuffer(a));a=read(a,"binary");assert("object"===typeof a);return a},"undefined"!=typeof scriptArgs?f.arguments=scriptArgs:"undefined"!=typeof arguments&&(f.arguments=arguments),"function"===typeof quit&&(f.quit=function(a){quit(a)});else if(v||w)w?A=self.location.href:document.currentScript&&(A=
|
||||||
|
document.currentScript.src),A=0!==A.indexOf("blob:")?A.substr(0,A.lastIndexOf("/")+1):"",f.read=function(a){var b=new XMLHttpRequest;b.open("GET",a,!1);b.send(null);return b.responseText},w&&(f.readBinary=function(a){var b=new XMLHttpRequest;b.open("GET",a,!1);b.responseType="arraybuffer";b.send(null);return new Uint8Array(b.response)}),f.readAsync=function(a,b,c){var d=new XMLHttpRequest;d.open("GET",a,!0);d.responseType="arraybuffer";d.onload=function(){200==d.status||0==d.status&&d.response?b(d.response):
|
||||||
|
c()};d.onerror=c;d.send(null)},f.setWindowTitle=function(a){document.title=a};var Aa=f.print||("undefined"!==typeof console?console.log.bind(console):"undefined"!==typeof print?print:null),C=f.printErr||("undefined"!==typeof printErr?printErr:"undefined"!==typeof console&&console.warn.bind(console)||Aa);for(u in wa)wa.hasOwnProperty(u)&&(f[u]=wa[u]);wa=void 0;function Ba(a){var b=D[Ca>>2];a=b+a+15&-16;if(a<=Da())D[Ca>>2]=a;else if(!Ea(a))return 0;return b}
|
||||||
|
var Fa={"f64-rem":function(a,b){return a%b},"debugger":function(){debugger}},Ga=1,E=Array(64);function ua(a){for(var b=0;64>b;b++)if(!E[b])return E[b]=a,Ga+b;throw"Finished up all reserved function pointers. Use a higher value for RESERVED_FUNCTION_POINTERS.";}"object"!==typeof WebAssembly&&C("no native wasm support detected");
|
||||||
|
function q(a,b){b=b||"i8";"*"===b.charAt(b.length-1)&&(b="i32");switch(b){case "i1":return l[a>>0];case "i8":return l[a>>0];case "i16":return Ha[a>>1];case "i32":return D[a>>2];case "i64":return D[a>>2];case "float":return Ja[a>>2];case "double":return Ka[a>>3];default:B("invalid type for getValue: "+b)}return null}var La,Ma=!1;function assert(a,b){a||B("Assertion failed: "+b)}function Na(a){var b=f["_"+a];assert(b,"Cannot call unknown function "+a+", make sure it is exported");return b}
|
||||||
|
function Oa(a,b,c,d){var e={string:function(a){var b=0;if(null!==a&&void 0!==a&&0!==a){var c=(a.length<<2)+1;b=h(c);r(a,F,b,c)}return b},array:function(a){var b=h(a.length);l.set(a,b);return b}},g=Na(a),k=[];a=0;if(d)for(var m=0;m<d.length;m++){var y=e[c[m]];y?(0===a&&(a=na()),k[m]=y(d[m])):k[m]=d[m]}c=g.apply(null,k);c=function(a){return"string"===b?G(a):"boolean"===b?!!a:a}(c);0!==a&&qa(a);return c}
|
||||||
|
function pa(a){var b="i32";"*"===b.charAt(b.length-1)&&(b="i32");switch(b){case "i1":l[a>>0]=0;break;case "i8":l[a>>0]=0;break;case "i16":Ha[a>>1]=0;break;case "i32":D[a>>2]=0;break;case "i64":aa=[0,1<=+Pa(0)?~~+Qa(0)>>>0:0];D[a>>2]=aa[0];D[a+4>>2]=aa[1];break;case "float":Ja[a>>2]=0;break;case "double":Ka[a>>3]=0;break;default:B("invalid type for setValue: "+b)}}var Ra=0,Sa=3;
|
||||||
|
function ea(a){var b=Ra;if("number"===typeof a){var c=!0;var d=a}else c=!1,d=a.length;b=b==Sa?e:[Ta,h,Ba][b](Math.max(d,1));if(c){var e=b;assert(0==(b&3));for(a=b+(d&-4);e<a;e+=4)D[e>>2]=0;for(a=b+d;e<a;)l[e++>>0]=0;return b}a.subarray||a.slice?F.set(a,b):F.set(new Uint8Array(a),b);return b}var Ua="undefined"!==typeof TextDecoder?new TextDecoder("utf8"):void 0;
|
||||||
|
function t(a,b,c){var d=b+c;for(c=b;a[c]&&!(c>=d);)++c;if(16<c-b&&a.subarray&&Ua)return Ua.decode(a.subarray(b,c));for(d="";b<c;){var e=a[b++];if(e&128){var g=a[b++]&63;if(192==(e&224))d+=String.fromCharCode((e&31)<<6|g);else{var k=a[b++]&63;e=224==(e&240)?(e&15)<<12|g<<6|k:(e&7)<<18|g<<12|k<<6|a[b++]&63;65536>e?d+=String.fromCharCode(e):(e-=65536,d+=String.fromCharCode(55296|e>>10,56320|e&1023))}}else d+=String.fromCharCode(e)}return d}function G(a){return a?t(F,a,void 0):""}
|
||||||
|
function r(a,b,c,d){if(!(0<d))return 0;var e=c;d=c+d-1;for(var g=0;g<a.length;++g){var k=a.charCodeAt(g);if(55296<=k&&57343>=k){var m=a.charCodeAt(++g);k=65536+((k&1023)<<10)|m&1023}if(127>=k){if(c>=d)break;b[c++]=k}else{if(2047>=k){if(c+1>=d)break;b[c++]=192|k>>6}else{if(65535>=k){if(c+2>=d)break;b[c++]=224|k>>12}else{if(c+3>=d)break;b[c++]=240|k>>18;b[c++]=128|k>>12&63}b[c++]=128|k>>6&63}b[c++]=128|k&63}}b[c]=0;return c-e}
|
||||||
|
function oa(a){for(var b=0,c=0;c<a.length;++c){var d=a.charCodeAt(c);55296<=d&&57343>=d&&(d=65536+((d&1023)<<10)|a.charCodeAt(++c)&1023);127>=d?++b:b=2047>=d?b+2:65535>=d?b+3:b+4}return b}"undefined"!==typeof TextDecoder&&new TextDecoder("utf-16le");function Va(a){return a.replace(/__Z[\w\d_]+/g,function(a){return a===a?a:a+" ["+a+"]"})}function Wa(a){0<a%65536&&(a+=65536-a%65536);return a}var buffer,l,F,Ha,D,Ja,Ka;
|
||||||
|
function Xa(){f.HEAP8=l=new Int8Array(buffer);f.HEAP16=Ha=new Int16Array(buffer);f.HEAP32=D=new Int32Array(buffer);f.HEAPU8=F=new Uint8Array(buffer);f.HEAPU16=new Uint16Array(buffer);f.HEAPU32=new Uint32Array(buffer);f.HEAPF32=Ja=new Float32Array(buffer);f.HEAPF64=Ka=new Float64Array(buffer)}var Ca=60128,Ya=f.TOTAL_MEMORY||16777216;5242880>Ya&&C("TOTAL_MEMORY should be larger than TOTAL_STACK, was "+Ya+"! (TOTAL_STACK=5242880)");
|
||||||
|
f.buffer?buffer=f.buffer:"object"===typeof WebAssembly&&"function"===typeof WebAssembly.Memory?(La=new WebAssembly.Memory({initial:Ya/65536}),buffer=La.buffer):buffer=new ArrayBuffer(Ya);Xa();D[Ca>>2]=5303264;function Za(a){for(;0<a.length;){var b=a.shift();if("function"==typeof b)b();else{var c=b.rc;"number"===typeof c?void 0===b.Fb?f.dynCall_v(c):f.dynCall_vi(c,b.Fb):c(void 0===b.Fb?null:b.Fb)}}}var $a=[],ab=[],bb=[],cb=[],db=!1;function eb(){var a=f.preRun.shift();$a.unshift(a)}
|
||||||
|
var Pa=Math.abs,Qa=Math.ceil,H=0,fb=null,gb=null;f.preloadedImages={};f.preloadedAudios={};function hb(){var a=I;return String.prototype.startsWith?a.startsWith("data:application/octet-stream;base64,"):0===a.indexOf("data:application/octet-stream;base64,")}var I="sql-wasm.wasm";if(!hb()){var jb=I;I=f.locateFile?f.locateFile(jb,A):A+jb}
|
||||||
|
function kb(){try{if(f.wasmBinary)return new Uint8Array(f.wasmBinary);if(f.readBinary)return f.readBinary(I);throw"both async and sync fetching of the wasm failed";}catch(a){B(a)}}function lb(){return f.wasmBinary||!v&&!w||"function"!==typeof fetch?new Promise(function(a){a(kb())}):fetch(I,{credentials:"same-origin"}).then(function(a){if(!a.ok)throw"failed to load wasm binary file at '"+I+"'";return a.arrayBuffer()}).catch(function(){return kb()})}
|
||||||
|
function mb(a){function b(a){f.asm=a.exports;H--;f.monitorRunDependencies&&f.monitorRunDependencies(H);0==H&&(null!==fb&&(clearInterval(fb),fb=null),gb&&(a=gb,gb=null,a()))}function c(a){b(a.instance)}function d(a){lb().then(function(a){return WebAssembly.instantiate(a,e)}).then(a,function(a){C("failed to asynchronously prepare wasm: "+a);B(a)})}var e={env:a,global:{NaN:NaN,Infinity:Infinity},"global.Math":Math,asm2wasm:Fa};H++;f.monitorRunDependencies&&f.monitorRunDependencies(H);if(f.instantiateWasm)try{return f.instantiateWasm(e,
|
||||||
|
b)}catch(g){return C("Module.instantiateWasm callback failed with error: "+g),!1}f.wasmBinary||"function"!==typeof WebAssembly.instantiateStreaming||hb()||"function"!==typeof fetch?d(c):WebAssembly.instantiateStreaming(fetch(I,{credentials:"same-origin"}),e).then(c,function(a){C("wasm streaming compile failed: "+a);C("falling back to ArrayBuffer instantiation");d(c)});return{}}
|
||||||
|
f.asm=function(a,b){b.memory=La;b.table=new WebAssembly.Table({initial:2560,maximum:2560,element:"anyfunc"});b.__memory_base=1024;b.__table_base=0;return mb(b)};ab.push({rc:function(){nb()}});var J={};
|
||||||
|
function ob(a){if(ob.rb){var b=D[a>>2];var c=D[b>>2]}else ob.rb=!0,J.USER=J.LOGNAME="web_user",J.PATH="/",J.PWD="/",J.HOME="/home/web_user",J.LANG="C.UTF-8",J._=f.thisProgram,c=db?Ta(1024):Ba(1024),b=db?Ta(256):Ba(256),D[b>>2]=c,D[a>>2]=b;a=[];var d=0,e;for(e in J)if("string"===typeof J[e]){var g=e+"="+J[e];a.push(g);d+=g.length}if(1024<d)throw Error("Environment size exceeded TOTAL_ENV_SIZE!");for(e=0;e<a.length;e++){d=g=a[e];for(var k=c,m=0;m<d.length;++m)l[k++>>0]=d.charCodeAt(m);l[k>>0]=0;D[b+
|
||||||
|
4*e>>2]=c;c+=g.length+1}D[b+4*a.length>>2]=0}function pb(a){f.___errno_location&&(D[f.___errno_location()>>2]=a);return a}function qb(a,b){for(var c=0,d=a.length-1;0<=d;d--){var e=a[d];"."===e?a.splice(d,1):".."===e?(a.splice(d,1),c++):c&&(a.splice(d,1),c--)}if(b)for(;c;c--)a.unshift("..");return a}function rb(a){var b="/"===a.charAt(0),c="/"===a.substr(-1);(a=qb(a.split("/").filter(function(a){return!!a}),!b).join("/"))||b||(a=".");a&&c&&(a+="/");return(b?"/":"")+a}
|
||||||
|
function sb(a){var b=/^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/.exec(a).slice(1);a=b[0];b=b[1];if(!a&&!b)return".";b&&(b=b.substr(0,b.length-1));return a+b}function tb(a){if("/"===a)return"/";var b=a.lastIndexOf("/");return-1===b?a:a.substr(b+1)}function ub(){var a=Array.prototype.slice.call(arguments,0);return rb(a.join("/"))}function n(a,b){return rb(a+"/"+b)}
|
||||||
|
function vb(){for(var a="",b=!1,c=arguments.length-1;-1<=c&&!b;c--){b=0<=c?arguments[c]:"/";if("string"!==typeof b)throw new TypeError("Arguments to path.resolve must be strings");if(!b)return"";a=b+"/"+a;b="/"===b.charAt(0)}a=qb(a.split("/").filter(function(a){return!!a}),!b).join("/");return(b?"/":"")+a||"."}var wb=[];function xb(a,b){wb[a]={input:[],output:[],ub:b};yb(a,zb)}
|
||||||
|
var zb={open:function(a){var b=wb[a.node.rdev];if(!b)throw new K(L.Cb);a.tty=b;a.seekable=!1},close:function(a){a.tty.ub.flush(a.tty)},flush:function(a){a.tty.ub.flush(a.tty)},read:function(a,b,c,d){if(!a.tty||!a.tty.ub.Xb)throw new K(L.Ob);for(var e=0,g=0;g<d;g++){try{var k=a.tty.ub.Xb(a.tty)}catch(m){throw new K(L.Lb);}if(void 0===k&&0===e)throw new K(L.ac);if(null===k||void 0===k)break;e++;b[c+g]=k}e&&(a.node.timestamp=Date.now());return e},write:function(a,b,c,d){if(!a.tty||!a.tty.ub.Ib)throw new K(L.Ob);
|
||||||
|
try{for(var e=0;e<d;e++)a.tty.ub.Ib(a.tty,b[c+e])}catch(g){throw new K(L.Lb);}d&&(a.node.timestamp=Date.now());return e}},Ab={Xb:function(a){if(!a.input.length){var b=null;if(x){var c=new Buffer(256),d=0,e=process.stdin.fd;if("win32"!=process.platform){var g=!1;try{e=fs.openSync("/dev/stdin","r"),g=!0}catch(k){}}try{d=fs.readSync(e,c,0,256,null)}catch(k){if(-1!=k.toString().indexOf("EOF"))d=0;else throw k;}g&&fs.closeSync(e);0<d?b=c.slice(0,d).toString("utf-8"):b=null}else"undefined"!=typeof window&&
|
||||||
|
"function"==typeof window.prompt?(b=window.prompt("Input: "),null!==b&&(b+="\n")):"function"==typeof readline&&(b=readline(),null!==b&&(b+="\n"));if(!b)return null;a.input=ba(b,!0)}return a.input.shift()},Ib:function(a,b){null===b||10===b?(Aa(t(a.output,0)),a.output=[]):0!=b&&a.output.push(b)},flush:function(a){a.output&&0<a.output.length&&(Aa(t(a.output,0)),a.output=[])}},Bb={Ib:function(a,b){null===b||10===b?(C(t(a.output,0)),a.output=[]):0!=b&&a.output.push(b)},flush:function(a){a.output&&0<a.output.length&&
|
||||||
|
(C(t(a.output,0)),a.output=[])}},M={mb:null,jb:function(){return M.createNode(null,"/",16895,0)},createNode:function(a,b,c,d){if(24576===(c&61440)||4096===(c&61440))throw new K(L.dc);M.mb||(M.mb={dir:{node:{lb:M.ab.lb,hb:M.ab.hb,lookup:M.ab.lookup,vb:M.ab.vb,rename:M.ab.rename,unlink:M.ab.unlink,rmdir:M.ab.rmdir,readdir:M.ab.readdir,symlink:M.ab.symlink},stream:{ob:M.cb.ob}},file:{node:{lb:M.ab.lb,hb:M.ab.hb},stream:{ob:M.cb.ob,read:M.cb.read,write:M.cb.write,Pb:M.cb.Pb,zb:M.cb.zb,Ab:M.cb.Ab}},link:{node:{lb:M.ab.lb,
|
||||||
|
hb:M.ab.hb,readlink:M.ab.readlink},stream:{}},Sb:{node:{lb:M.ab.lb,hb:M.ab.hb},stream:Cb}});c=Db(a,b,c,d);N(c.mode)?(c.ab=M.mb.dir.node,c.cb=M.mb.dir.stream,c.bb={}):32768===(c.mode&61440)?(c.ab=M.mb.file.node,c.cb=M.mb.file.stream,c.gb=0,c.bb=null):40960===(c.mode&61440)?(c.ab=M.mb.link.node,c.cb=M.mb.link.stream):8192===(c.mode&61440)&&(c.ab=M.mb.Sb.node,c.cb=M.mb.Sb.stream);c.timestamp=Date.now();a&&(a.bb[b]=c);return c},ff:function(a){if(a.bb&&a.bb.subarray){for(var b=[],c=0;c<a.gb;++c)b.push(a.bb[c]);
|
||||||
|
return b}return a.bb},gf:function(a){return a.bb?a.bb.subarray?a.bb.subarray(0,a.gb):new Uint8Array(a.bb):new Uint8Array},Tb:function(a,b){var c=a.bb?a.bb.length:0;c>=b||(b=Math.max(b,c*(1048576>c?2:1.125)|0),0!=c&&(b=Math.max(b,256)),c=a.bb,a.bb=new Uint8Array(b),0<a.gb&&a.bb.set(c.subarray(0,a.gb),0))},yc:function(a,b){if(a.gb!=b)if(0==b)a.bb=null,a.gb=0;else{if(!a.bb||a.bb.subarray){var c=a.bb;a.bb=new Uint8Array(new ArrayBuffer(b));c&&a.bb.set(c.subarray(0,Math.min(b,a.gb)))}else if(a.bb||(a.bb=
|
||||||
|
[]),a.bb.length>b)a.bb.length=b;else for(;a.bb.length<b;)a.bb.push(0);a.gb=b}},ab:{lb:function(a){var b={};b.dev=8192===(a.mode&61440)?a.id:1;b.ino=a.id;b.mode=a.mode;b.nlink=1;b.uid=0;b.gid=0;b.rdev=a.rdev;N(a.mode)?b.size=4096:32768===(a.mode&61440)?b.size=a.gb:40960===(a.mode&61440)?b.size=a.link.length:b.size=0;b.atime=new Date(a.timestamp);b.mtime=new Date(a.timestamp);b.ctime=new Date(a.timestamp);b.pb=4096;b.blocks=Math.ceil(b.size/b.pb);return b},hb:function(a,b){void 0!==b.mode&&(a.mode=
|
||||||
|
b.mode);void 0!==b.timestamp&&(a.timestamp=b.timestamp);void 0!==b.size&&M.yc(a,b.size)},lookup:function(){throw Eb[L.bc];},vb:function(a,b,c,d){return M.createNode(a,b,c,d)},rename:function(a,b,c){if(N(a.mode)){try{var d=O(b,c)}catch(g){}if(d)for(var e in d.bb)throw new K(L.Nb);}delete a.parent.bb[a.name];a.name=c;b.bb[c]=a;a.parent=b},unlink:function(a,b){delete a.bb[b]},rmdir:function(a,b){var c=O(a,b),d;for(d in c.bb)throw new K(L.Nb);delete a.bb[b]},readdir:function(a){var b=[".",".."],c;for(c in a.bb)a.bb.hasOwnProperty(c)&&
|
||||||
|
b.push(c);return b},symlink:function(a,b,c){a=M.createNode(a,b,41471,0);a.link=c;return a},readlink:function(a){if(40960!==(a.mode&61440))throw new K(L.ib);return a.link}},cb:{read:function(a,b,c,d,e){var g=a.node.bb;if(e>=a.node.gb)return 0;a=Math.min(a.node.gb-e,d);if(8<a&&g.subarray)b.set(g.subarray(e,e+a),c);else for(d=0;d<a;d++)b[c+d]=g[e+d];return a},write:function(a,b,c,d,e,g){g=!1;if(!d)return 0;a=a.node;a.timestamp=Date.now();if(b.subarray&&(!a.bb||a.bb.subarray)){if(g)return a.bb=b.subarray(c,
|
||||||
|
c+d),a.gb=d;if(0===a.gb&&0===e)return a.bb=new Uint8Array(b.subarray(c,c+d)),a.gb=d;if(e+d<=a.gb)return a.bb.set(b.subarray(c,c+d),e),d}M.Tb(a,e+d);if(a.bb.subarray&&b.subarray)a.bb.set(b.subarray(c,c+d),e);else for(g=0;g<d;g++)a.bb[e+g]=b[c+g];a.gb=Math.max(a.gb,e+d);return d},ob:function(a,b,c){1===c?b+=a.position:2===c&&32768===(a.node.mode&61440)&&(b+=a.node.gb);if(0>b)throw new K(L.ib);return b},Pb:function(a,b,c){M.Tb(a.node,b+c);a.node.gb=Math.max(a.node.gb,b+c)},zb:function(a,b,c,d,e,g,k){if(32768!==
|
||||||
|
(a.node.mode&61440))throw new K(L.Cb);c=a.node.bb;if(k&2||c.buffer!==b&&c.buffer!==b.buffer){if(0<e||e+d<a.node.gb)c.subarray?c=c.subarray(e,e+d):c=Array.prototype.slice.call(c,e,e+d);a=!0;d=Ta(d);if(!d)throw new K(L.Mb);b.set(c,d)}else a=!1,d=c.byteOffset;return{xc:d,Db:a}},Ab:function(a,b,c,d,e){if(32768!==(a.node.mode&61440))throw new K(L.Cb);if(e&2)return 0;M.cb.write(a,b,0,d,c,!1);return 0}}},P={yb:!1,Ac:function(){P.yb=!!process.platform.match(/^win/);var a=process.binding("constants");a.fs&&
|
||||||
|
(a=a.fs);P.Ub={1024:a.O_APPEND,64:a.O_CREAT,128:a.O_EXCL,0:a.O_RDONLY,2:a.O_RDWR,4096:a.O_SYNC,512:a.O_TRUNC,1:a.O_WRONLY}},Rb:function(a){return Buffer.rb?Buffer.from(a):new Buffer(a)},jb:function(a){assert(x);return P.createNode(null,"/",P.Wb(a.Hb.root),0)},createNode:function(a,b,c){if(!N(c)&&32768!==(c&61440)&&40960!==(c&61440))throw new K(L.ib);a=Db(a,b,c);a.ab=P.ab;a.cb=P.cb;return a},Wb:function(a){try{var b=fs.lstatSync(a);P.yb&&(b.mode=b.mode|(b.mode&292)>>2)}catch(c){if(!c.code)throw c;
|
||||||
|
throw new K(L[c.code]);}return b.mode},kb:function(a){for(var b=[];a.parent!==a;)b.push(a.name),a=a.parent;b.push(a.jb.Hb.root);b.reverse();return ub.apply(null,b)},qc:function(a){a&=-2656257;var b=0,c;for(c in P.Ub)a&c&&(b|=P.Ub[c],a^=c);if(a)throw new K(L.ib);return b},ab:{lb:function(a){a=P.kb(a);try{var b=fs.lstatSync(a)}catch(c){if(!c.code)throw c;throw new K(L[c.code]);}P.yb&&!b.pb&&(b.pb=4096);P.yb&&!b.blocks&&(b.blocks=(b.size+b.pb-1)/b.pb|0);return{dev:b.dev,ino:b.ino,mode:b.mode,nlink:b.nlink,
|
||||||
|
uid:b.uid,gid:b.gid,rdev:b.rdev,size:b.size,atime:b.atime,mtime:b.mtime,ctime:b.ctime,pb:b.pb,blocks:b.blocks}},hb:function(a,b){var c=P.kb(a);try{void 0!==b.mode&&(fs.chmodSync(c,b.mode),a.mode=b.mode),void 0!==b.size&&fs.truncateSync(c,b.size)}catch(d){if(!d.code)throw d;throw new K(L[d.code]);}},lookup:function(a,b){var c=n(P.kb(a),b);c=P.Wb(c);return P.createNode(a,b,c)},vb:function(a,b,c,d){a=P.createNode(a,b,c,d);b=P.kb(a);try{N(a.mode)?fs.mkdirSync(b,a.mode):fs.writeFileSync(b,"",{mode:a.mode})}catch(e){if(!e.code)throw e;
|
||||||
|
throw new K(L[e.code]);}return a},rename:function(a,b,c){a=P.kb(a);b=n(P.kb(b),c);try{fs.renameSync(a,b)}catch(d){if(!d.code)throw d;throw new K(L[d.code]);}},unlink:function(a,b){a=n(P.kb(a),b);try{fs.unlinkSync(a)}catch(c){if(!c.code)throw c;throw new K(L[c.code]);}},rmdir:function(a,b){a=n(P.kb(a),b);try{fs.rmdirSync(a)}catch(c){if(!c.code)throw c;throw new K(L[c.code]);}},readdir:function(a){a=P.kb(a);try{return fs.readdirSync(a)}catch(b){if(!b.code)throw b;throw new K(L[b.code]);}},symlink:function(a,
|
||||||
|
b,c){a=n(P.kb(a),b);try{fs.symlinkSync(c,a)}catch(d){if(!d.code)throw d;throw new K(L[d.code]);}},readlink:function(a){var b=P.kb(a);try{return b=fs.readlinkSync(b),b=Fb.relative(Fb.resolve(a.jb.Hb.root),b)}catch(c){if(!c.code)throw c;throw new K(L[c.code]);}}},cb:{open:function(a){var b=P.kb(a.node);try{32768===(a.node.mode&61440)&&(a.wb=fs.openSync(b,P.qc(a.flags)))}catch(c){if(!c.code)throw c;throw new K(L[c.code]);}},close:function(a){try{32768===(a.node.mode&61440)&&a.wb&&fs.closeSync(a.wb)}catch(b){if(!b.code)throw b;
|
||||||
|
throw new K(L[b.code]);}},read:function(a,b,c,d,e){if(0===d)return 0;try{return fs.readSync(a.wb,P.Rb(b.buffer),c,d,e)}catch(g){throw new K(L[g.code]);}},write:function(a,b,c,d,e){try{return fs.writeSync(a.wb,P.Rb(b.buffer),c,d,e)}catch(g){throw new K(L[g.code]);}},ob:function(a,b,c){if(1===c)b+=a.position;else if(2===c&&32768===(a.node.mode&61440))try{b+=fs.fstatSync(a.wb).size}catch(d){throw new K(L[d.code]);}if(0>b)throw new K(L.ib);return b}}},Gb=null,Hb={},Q=[],Ib=1,R=null,Jb=!0,S={},K=null,
|
||||||
|
Eb={};function T(a,b){a=vb("/",a);b=b||{};if(!a)return{path:"",node:null};var c={Vb:!0,Jb:0},d;for(d in c)void 0===b[d]&&(b[d]=c[d]);if(8<b.Jb)throw new K(40);a=qb(a.split("/").filter(function(a){return!!a}),!1);var e=Gb;c="/";for(d=0;d<a.length;d++){var g=d===a.length-1;if(g&&b.parent)break;e=O(e,a[d]);c=n(c,a[d]);e.sb&&(!g||g&&b.Vb)&&(e=e.sb.root);if(!g||b.qb)for(g=0;40960===(e.mode&61440);)if(e=Kb(c),c=vb(sb(c),e),e=T(c,{Jb:b.Jb}).node,40<g++)throw new K(40);}return{path:c,node:e}}
|
||||||
|
function Lb(a){for(var b;;){if(a===a.parent)return a=a.jb.Yb,b?"/"!==a[a.length-1]?a+"/"+b:a+b:a;b=b?a.name+"/"+b:a.name;a=a.parent}}function Mb(a,b){for(var c=0,d=0;d<b.length;d++)c=(c<<5)-c+b.charCodeAt(d)|0;return(a+c>>>0)%R.length}function Nb(a){var b=Mb(a.parent.id,a.name);a.tb=R[b];R[b]=a}function Ob(a){var b=Mb(a.parent.id,a.name);if(R[b]===a)R[b]=a.tb;else for(b=R[b];b;){if(b.tb===a){b.tb=a.tb;break}b=b.tb}}
|
||||||
|
function O(a,b){var c;if(c=(c=Pb(a,"x"))?c:a.ab.lookup?0:13)throw new K(c,a);for(c=R[Mb(a.id,b)];c;c=c.tb){var d=c.name;if(c.parent.id===a.id&&d===b)return c}return a.ab.lookup(a,b)}
|
||||||
|
function Db(a,b,c,d){Qb||(Qb=function(a,b,c,d){a||(a=this);this.parent=a;this.jb=a.jb;this.sb=null;this.id=Ib++;this.name=b;this.mode=c;this.ab={};this.cb={};this.rdev=d},Qb.prototype={},Object.defineProperties(Qb.prototype,{read:{get:function(){return 365===(this.mode&365)},set:function(a){a?this.mode|=365:this.mode&=-366}},write:{get:function(){return 146===(this.mode&146)},set:function(a){a?this.mode|=146:this.mode&=-147}}}));a=new Qb(a,b,c,d);Nb(a);return a}
|
||||||
|
function N(a){return 16384===(a&61440)}var Rb={r:0,rs:1052672,"r+":2,w:577,wx:705,xw:705,"w+":578,"wx+":706,"xw+":706,a:1089,ax:1217,xa:1217,"a+":1090,"ax+":1218,"xa+":1218};function ic(a){var b=["r","w","rw"][a&3];a&512&&(b+="w");return b}function Pb(a,b){if(Jb)return 0;if(-1===b.indexOf("r")||a.mode&292){if(-1!==b.indexOf("w")&&!(a.mode&146)||-1!==b.indexOf("x")&&!(a.mode&73))return 13}else return 13;return 0}function tc(a,b){try{return O(a,b),17}catch(c){}return Pb(a,"wx")}
|
||||||
|
function uc(a,b,c){try{var d=O(a,b)}catch(e){return e.eb}if(a=Pb(a,"wx"))return a;if(c){if(!N(d.mode))return 20;if(d===d.parent||"/"===Lb(d))return 16}else if(N(d.mode))return 21;return 0}function vc(a){var b=4096;for(a=a||0;a<=b;a++)if(!Q[a])return a;throw new K(24);}
|
||||||
|
function wc(a,b){xc||(xc=function(){},xc.prototype={},Object.defineProperties(xc.prototype,{object:{get:function(){return this.node},set:function(a){this.node=a}}}));var c=new xc,d;for(d in a)c[d]=a[d];a=c;b=vc(b);a.fd=b;return Q[b]=a}var Cb={open:function(a){a.cb=Hb[a.node.rdev].cb;a.cb.open&&a.cb.open(a)},ob:function(){throw new K(29);}};function yb(a,b){Hb[a]={cb:b}}
|
||||||
|
function yc(a,b){var c="/"===b,d=!b;if(c&&Gb)throw new K(16);if(!c&&!d){var e=T(b,{Vb:!1});b=e.path;e=e.node;if(e.sb)throw new K(16);if(!N(e.mode))throw new K(20);}b={type:a,Hb:{},Yb:b,wc:[]};a=a.jb(b);a.jb=b;b.root=a;c?Gb=a:e&&(e.sb=b,e.jb&&e.jb.wc.push(b))}function ja(a,b,c){var d=T(a,{parent:!0}).node;a=tb(a);if(!a||"."===a||".."===a)throw new K(22);var e=tc(d,a);if(e)throw new K(e);if(!d.ab.vb)throw new K(1);return d.ab.vb(d,a,b,c)}function U(a,b){ja(a,(void 0!==b?b:511)&1023|16384,0)}
|
||||||
|
function zc(a,b,c){"undefined"===typeof c&&(c=b,b=438);ja(a,b|8192,c)}function Ac(a,b){if(!vb(a))throw new K(2);var c=T(b,{parent:!0}).node;if(!c)throw new K(2);b=tb(b);var d=tc(c,b);if(d)throw new K(d);if(!c.ab.symlink)throw new K(1);c.ab.symlink(c,b,a)}
|
||||||
|
function ta(a){var b=T(a,{parent:!0}).node,c=tb(a),d=O(b,c),e=uc(b,c,!1);if(e)throw new K(e);if(!b.ab.unlink)throw new K(1);if(d.sb)throw new K(16);try{S.willDeletePath&&S.willDeletePath(a)}catch(g){console.log("FS.trackingDelegate['willDeletePath']('"+a+"') threw an exception: "+g.message)}b.ab.unlink(b,c);Ob(d);try{if(S.onDeletePath)S.onDeletePath(a)}catch(g){console.log("FS.trackingDelegate['onDeletePath']('"+a+"') threw an exception: "+g.message)}}
|
||||||
|
function Kb(a){a=T(a).node;if(!a)throw new K(2);if(!a.ab.readlink)throw new K(22);return vb(Lb(a.parent),a.ab.readlink(a))}function ra(a,b){a=T(a,{qb:!b}).node;if(!a)throw new K(2);if(!a.ab.lb)throw new K(1);return a.ab.lb(a)}function Bc(a){return ra(a,!0)}function ka(a,b){var c;"string"===typeof a?c=T(a,{qb:!0}).node:c=a;if(!c.ab.hb)throw new K(1);c.ab.hb(c,{mode:b&4095|c.mode&-4096,timestamp:Date.now()})}
|
||||||
|
function Cc(a){var b;"string"===typeof a?b=T(a,{qb:!0}).node:b=a;if(!b.ab.hb)throw new K(1);b.ab.hb(b,{timestamp:Date.now()})}function Dc(a,b){if(0>b)throw new K(22);var c;"string"===typeof a?c=T(a,{qb:!0}).node:c=a;if(!c.ab.hb)throw new K(1);if(N(c.mode))throw new K(21);if(32768!==(c.mode&61440))throw new K(22);if(a=Pb(c,"w"))throw new K(a);c.ab.hb(c,{size:b,timestamp:Date.now()})}
|
||||||
|
function p(a,b,c,d){if(""===a)throw new K(2);if("string"===typeof b){var e=Rb[b];if("undefined"===typeof e)throw Error("Unknown file open mode: "+b);b=e}c=b&64?("undefined"===typeof c?438:c)&4095|32768:0;if("object"===typeof a)var g=a;else{a=rb(a);try{g=T(a,{qb:!(b&131072)}).node}catch(k){}}e=!1;if(b&64)if(g){if(b&128)throw new K(17);}else g=ja(a,c,0),e=!0;if(!g)throw new K(2);8192===(g.mode&61440)&&(b&=-513);if(b&65536&&!N(g.mode))throw new K(20);if(!e&&(c=g?40960===(g.mode&61440)?40:N(g.mode)&&
|
||||||
|
("r"!==ic(b)||b&512)?21:Pb(g,ic(b)):2))throw new K(c);b&512&&Dc(g,0);b&=-641;d=wc({node:g,path:Lb(g),flags:b,seekable:!0,position:0,cb:g.cb,Bc:[],error:!1},d);d.cb.open&&d.cb.open(d);!f.logReadFiles||b&1||(Ec||(Ec={}),a in Ec||(Ec[a]=1,console.log("FS.trackingDelegate error on read file: "+a)));try{S.onOpenFile&&(g=0,1!==(b&2097155)&&(g|=1),0!==(b&2097155)&&(g|=2),S.onOpenFile(a,g))}catch(k){console.log("FS.trackingDelegate['onOpenFile']('"+a+"', flags) threw an exception: "+k.message)}return d}
|
||||||
|
function ma(a){if(null===a.fd)throw new K(9);a.Gb&&(a.Gb=null);try{a.cb.close&&a.cb.close(a)}catch(b){throw b;}finally{Q[a.fd]=null}a.fd=null}function Fc(a,b,c){if(null===a.fd)throw new K(9);if(!a.seekable||!a.cb.ob)throw new K(29);if(0!=c&&1!=c&&2!=c)throw new K(22);a.position=a.cb.ob(a,b,c);a.Bc=[]}
|
||||||
|
function sa(a,b,c,d,e){if(0>d||0>e)throw new K(22);if(null===a.fd)throw new K(9);if(1===(a.flags&2097155))throw new K(9);if(N(a.node.mode))throw new K(21);if(!a.cb.read)throw new K(22);var g="undefined"!==typeof e;if(!g)e=a.position;else if(!a.seekable)throw new K(29);b=a.cb.read(a,b,c,d,e);g||(a.position+=b);return b}
|
||||||
|
function la(a,b,c,d,e,g){if(0>d||0>e)throw new K(22);if(null===a.fd)throw new K(9);if(0===(a.flags&2097155))throw new K(9);if(N(a.node.mode))throw new K(21);if(!a.cb.write)throw new K(22);a.flags&1024&&Fc(a,0,2);var k="undefined"!==typeof e;if(!k)e=a.position;else if(!a.seekable)throw new K(29);b=a.cb.write(a,b,c,d,e,g);k||(a.position+=b);try{if(a.path&&S.onWriteToFile)S.onWriteToFile(a.path)}catch(m){console.log("FS.trackingDelegate['onWriteToFile']('"+a.path+"') threw an exception: "+m.message)}return b}
|
||||||
|
function Gc(){K||(K=function(a,b){this.node=b;this.zc=function(a){this.eb=a};this.zc(a);this.message="FS error";this.stack&&Object.defineProperty(this,"stack",{value:Error().stack,writable:!0})},K.prototype=Error(),K.prototype.constructor=K,[2].forEach(function(a){Eb[a]=new K(a);Eb[a].stack="<generic error, no stack>"}))}var Hc;function ia(a,b){var c=0;a&&(c|=365);b&&(c|=146);return c}
|
||||||
|
function Ic(a,b,c){a=n("/dev",a);var d=ia(!!b,!!c);Jc||(Jc=64);var e=Jc++<<8|0;yb(e,{open:function(a){a.seekable=!1},close:function(){c&&c.buffer&&c.buffer.length&&c(10)},read:function(a,c,d,e){for(var g=0,k=0;k<e;k++){try{var m=b()}catch(Ia){throw new K(5);}if(void 0===m&&0===g)throw new K(11);if(null===m||void 0===m)break;g++;c[d+k]=m}g&&(a.node.timestamp=Date.now());return g},write:function(a,b,d,e){for(var g=0;g<e;g++)try{c(b[d+g])}catch(fa){throw new K(5);}e&&(a.node.timestamp=Date.now());return g}});
|
||||||
|
zc(a,d,e)}
|
||||||
|
var Jc,V={},Qb,xc,Ec,L={dc:1,bc:2,Ae:3,sd:4,Lb:5,Ob:6,Ic:7,Td:8,Kb:9,Xc:10,ac:11,Ke:11,Mb:12,$b:13,ld:14,ee:15,Vc:16,kd:17,Le:18,Cb:19,cc:20,ud:21,ib:22,Od:23,Gd:24,je:25,He:26,md:27,ae:28,ze:29,ve:30,Hd:31,pe:32,gd:33,ec:34,Xd:42,pd:43,Yc:44,wd:45,xd:46,yd:47,Ed:48,Ie:49,Rd:50,vd:51,cd:35,Ud:37,Oc:52,Rc:53,Me:54,Pd:55,Sc:56,Tc:57,dd:35,Uc:59,ce:60,Sd:61,Ee:62,be:63,Yd:64,Zd:65,ue:66,Vd:67,Lc:68,Be:69,Zc:70,qe:71,Jd:72,hd:73,Qc:74,ke:76,Pc:77,te:78,zd:79,Ad:80,Dd:81,Cd:82,Bd:83,de:38,Nb:39,Kd:36,
|
||||||
|
Fd:40,le:95,oe:96,bd:104,Qd:105,Mc:97,se:91,he:88,$d:92,xe:108,ad:111,Jc:98,$c:103,Nd:101,Ld:100,Fe:110,nd:112,od:113,rd:115,Nc:114,ed:89,Id:90,re:93,ye:94,Kc:99,Md:102,td:106,fe:107,Ge:109,Je:87,jd:122,Ce:116,ie:95,Wd:123,qd:84,me:75,Wc:125,ge:131,ne:130,De:86},Kc={};
|
||||||
|
function Lc(a,b,c){try{var d=a(b)}catch(e){if(e&&e.node&&rb(b)!==rb(Lb(e.node)))return-L.cc;throw e;}D[c>>2]=d.dev;D[c+4>>2]=0;D[c+8>>2]=d.ino;D[c+12>>2]=d.mode;D[c+16>>2]=d.nlink;D[c+20>>2]=d.uid;D[c+24>>2]=d.gid;D[c+28>>2]=d.rdev;D[c+32>>2]=0;D[c+36>>2]=d.size;D[c+40>>2]=4096;D[c+44>>2]=d.blocks;D[c+48>>2]=d.atime.getTime()/1E3|0;D[c+52>>2]=0;D[c+56>>2]=d.mtime.getTime()/1E3|0;D[c+60>>2]=0;D[c+64>>2]=d.ctime.getTime()/1E3|0;D[c+68>>2]=0;D[c+72>>2]=d.ino;return 0}var W=0;
|
||||||
|
function X(){W+=4;return D[W-4>>2]}function Y(){return G(X())}function Z(){var a=Q[X()];if(!a)throw new K(L.Kb);return a}function Da(){return l.length}function Ea(a){if(2147418112<a)return!1;for(var b=Math.max(Da(),16777216);b<a;)536870912>=b?b=Wa(2*b):b=Math.min(Wa((3*b+2147483648)/4),2147418112);a=Wa(b);var c=buffer.byteLength;try{var d=-1!==La.grow((a-c)/65536)?buffer=La.buffer:null}catch(e){d=null}if(!d||d.byteLength!=b)return!1;Xa();return!0}
|
||||||
|
function Mc(a){if(0===a)return 0;a=G(a);if(!J.hasOwnProperty(a))return 0;Mc.rb&&ha(Mc.rb);a=J[a];var b=oa(a)+1,c=Ta(b);c&&r(a,l,c,b);Mc.rb=c;return Mc.rb}r("GMT",F,60272,4);
|
||||||
|
function Nc(){function a(a){return(a=a.toTimeString().match(/\(([A-Za-z ]+)\)$/))?a[1]:"GMT"}if(!Oc){Oc=!0;D[Pc()>>2]=60*(new Date).getTimezoneOffset();var b=new Date(2E3,0,1),c=new Date(2E3,6,1);D[Qc()>>2]=Number(b.getTimezoneOffset()!=c.getTimezoneOffset());var d=a(b),e=a(c);d=ea(ba(d));e=ea(ba(e));c.getTimezoneOffset()<b.getTimezoneOffset()?(D[Rc()>>2]=d,D[Rc()+4>>2]=e):(D[Rc()>>2]=e,D[Rc()+4>>2]=d)}}var Oc;
|
||||||
|
function Sc(a){a/=1E3;if((v||w)&&self.performance&&self.performance.now)for(var b=self.performance.now();self.performance.now()-b<a;);else for(b=Date.now();Date.now()-b<a;);return 0}f._usleep=Sc;Gc();R=Array(4096);yc(M,"/");U("/tmp");U("/home");U("/home/web_user");
|
||||||
|
(function(){U("/dev");yb(259,{read:function(){return 0},write:function(a,b,c,k){return k}});zc("/dev/null",259);xb(1280,Ab);xb(1536,Bb);zc("/dev/tty",1280);zc("/dev/tty1",1536);if("object"===typeof crypto&&"function"===typeof crypto.getRandomValues){var a=new Uint8Array(1);var b=function(){crypto.getRandomValues(a);return a[0]}}else if(x)try{var c=require("crypto");b=function(){return c.randomBytes(1)[0]}}catch(d){}b||(b=function(){B("random_device")});Ic("random",b);Ic("urandom",b);U("/dev/shm");
|
||||||
|
U("/dev/shm/tmp")})();U("/proc");U("/proc/self");U("/proc/self/fd");yc({jb:function(){var a=Db("/proc/self","fd",16895,73);a.ab={lookup:function(a,c){var b=Q[+c];if(!b)throw new K(9);a={parent:null,jb:{Yb:"fake"},ab:{readlink:function(){return b.path}}};return a.parent=a}};return a}},"/proc/self/fd");if(x){var fs=require("fs"),Fb=require("path");P.Ac()}function ba(a,b){var c=Array(oa(a)+1);a=r(a,c,0,c.length);b&&(c.length=a);return c}
|
||||||
|
var Vc=f.asm({},{n:B,l:function(a){return E[a]()},i:function(a,b){return E[a](b)},h:function(a,b,c){return E[a](b,c)},g:function(a,b,c,d){return E[a](b,c,d)},f:function(a,b,c,d,e){return E[a](b,c,d,e)},e:function(a,b,c,d,e,g){return E[a](b,c,d,e,g)},d:function(a,b,c,d,e,g,k){return E[a](b,c,d,e,g,k)},B:function(a,b,c,d,e){return E[a](b,c,d,e)},A:function(a,b,c){return E[a](b,c)},z:function(a,b,c,d){return E[a](b,c,d)},y:function(a,b,c,d,e){return E[a](b,c,d,e)},c:function(a,b){E[a](b)},b:function(a,
|
||||||
|
b,c){E[a](b,c)},k:function(a,b,c,d){E[a](b,c,d)},j:function(a,b,c,d,e){E[a](b,c,d,e)},x:function(a,b,c,d,e,g){E[a](b,c,d,e,g)},w:function(a,b,c,d){E[a](b,c,d)},v:function(a,b,c,d){E[a](b,c,d)},m:function(a,b,c,d){B("Assertion failed: "+G(a)+", at: "+[b?G(b):"unknown filename",c,d?G(d):"unknown function"])},ga:ob,u:pb,fa:function(a,b){W=b;try{var c=Y();ta(c);return 0}catch(d){return"undefined"!==typeof V&&d instanceof K||B(d),-d.eb}},ea:function(a,b){W=b;try{return Z(),0}catch(c){return"undefined"!==
|
||||||
|
typeof V&&c instanceof K||B(c),-c.eb}},da:function(a,b){W=b;try{var c=Z();X();var d=X(),e=X(),g=X();Fc(c,d,g);D[e>>2]=c.position;c.Gb&&0===d&&0===g&&(c.Gb=null);return 0}catch(k){return"undefined"!==typeof V&&k instanceof K||B(k),-k.eb}},ca:function(a,b){W=b;try{var c=Y(),d=X();ka(c,d);return 0}catch(e){return"undefined"!==typeof V&&e instanceof K||B(e),-e.eb}},ba:function(a,b){W=b;try{var c=X(),d=X();if(0===d)return-L.ib;if(d<oa("/")+1)return-L.ec;r("/",F,c,d);return c}catch(e){return"undefined"!==
|
||||||
|
typeof V&&e instanceof K||B(e),-e.eb}},aa:function(a,b){W=b;try{var c=X(),d=X(),e=X(),g=X(),k=X(),m=X();m<<=12;a=!1;if(-1===k){var y=Tc(16384,d);if(!y)return-L.Mb;Uc(y,0,d);a=!0}else{var z=Q[k];if(!z)return-L.Kb;b=F;if(1===(z.flags&2097155))throw new K(13);if(!z.cb.zb)throw new K(19);var fa=z.cb.zb(z,b,c,d,m,e,g);y=fa.xc;a=fa.Db}Kc[y]={vc:y,uc:d,Db:a,fd:k,flags:g};return y}catch(ca){return"undefined"!==typeof V&&ca instanceof K||B(ca),-ca.eb}},$:function(a,b){W=b;try{var c=X();X();var d=X();X();var e=
|
||||||
|
Q[c];if(!e)throw new K(9);if(0===(e.flags&2097155))throw new K(22);Dc(e.node,d);return 0}catch(g){return"undefined"!==typeof V&&g instanceof K||B(g),-g.eb}},t:function(a,b){W=b;try{var c=Y(),d=X();return Lc(ra,c,d)}catch(e){return"undefined"!==typeof V&&e instanceof K||B(e),-e.eb}},_:function(a,b){W=b;try{var c=Y(),d=X();return Lc(Bc,c,d)}catch(e){return"undefined"!==typeof V&&e instanceof K||B(e),-e.eb}},Z:function(a,b){W=b;try{var c=Z(),d=X();return Lc(ra,c.path,d)}catch(e){return"undefined"!==
|
||||||
|
typeof V&&e instanceof K||B(e),-e.eb}},Y:function(a,b){W=b;return 42},X:function(a,b){W=b;return 0},W:function(a,b){W=b;try{var c=X();X();X();var d=Q[c];if(!d)throw new K(9);Cc(d.node);return 0}catch(e){return"undefined"!==typeof V&&e instanceof K||B(e),-e.eb}},V:function(a,b){W=b;try{var c=Y();X();X();Cc(c);return 0}catch(d){return"undefined"!==typeof V&&d instanceof K||B(d),-d.eb}},o:function(a,b){W=b;try{var c=Z();switch(X()){case 0:var d=X();return 0>d?-L.ib:p(c.path,c.flags,0,d).fd;case 1:case 2:return 0;
|
||||||
|
case 3:return c.flags;case 4:return d=X(),c.flags|=d,0;case 12:return d=X(),Ha[d+0>>1]=2,0;case 13:case 14:return 0;case 16:case 8:return-L.ib;case 9:return pb(L.ib),-1;default:return-L.ib}}catch(e){return"undefined"!==typeof V&&e instanceof K||B(e),-e.eb}},U:function(a,b){W=b;try{var c=Z(),d=X(),e=X();return sa(c,l,d,e)}catch(g){return"undefined"!==typeof V&&g instanceof K||B(g),-g.eb}},T:function(a,b){W=b;try{var c=Y();var d=X();if(d&-8)var e=-L.ib;else{var g=T(c,{qb:!0}).node;a="";d&4&&(a+="r");
|
||||||
|
d&2&&(a+="w");d&1&&(a+="x");e=a&&Pb(g,a)?-L.$b:0}return e}catch(k){return"undefined"!==typeof V&&k instanceof K||B(k),-k.eb}},S:function(a,b){W=b;try{var c=Y(),d=X();a=c;a=rb(a);"/"===a[a.length-1]&&(a=a.substr(0,a.length-1));U(a,d);return 0}catch(e){return"undefined"!==typeof V&&e instanceof K||B(e),-e.eb}},R:function(a,b){W=b;try{var c=Z(),d=X(),e=X();return la(c,l,d,e)}catch(g){return"undefined"!==typeof V&&g instanceof K||B(g),-g.eb}},Q:function(a,b){W=b;try{var c=Y(),d=T(c,{parent:!0}).node,
|
||||||
|
e=tb(c),g=O(d,e),k=uc(d,e,!0);if(k)throw new K(k);if(!d.ab.rmdir)throw new K(1);if(g.sb)throw new K(16);try{S.willDeletePath&&S.willDeletePath(c)}catch(m){console.log("FS.trackingDelegate['willDeletePath']('"+c+"') threw an exception: "+m.message)}d.ab.rmdir(d,e);Ob(g);try{if(S.onDeletePath)S.onDeletePath(c)}catch(m){console.log("FS.trackingDelegate['onDeletePath']('"+c+"') threw an exception: "+m.message)}return 0}catch(m){return"undefined"!==typeof V&&m instanceof K||B(m),-m.eb}},P:function(a,b){W=
|
||||||
|
b;try{var c=Y(),d=X(),e=X();return p(c,d,e).fd}catch(g){return"undefined"!==typeof V&&g instanceof K||B(g),-g.eb}},s:function(a,b){W=b;try{var c=Z();ma(c);return 0}catch(d){return"undefined"!==typeof V&&d instanceof K||B(d),-d.eb}},O:function(a,b){W=b;try{var c=Y(),d=X();var e=X();if(0>=e)var g=-L.ib;else{var k=Kb(c),m=Math.min(e,oa(k)),y=l[d+m];r(k,F,d,e+1);l[d+m]=y;g=m}return g}catch(z){return"undefined"!==typeof V&&z instanceof K||B(z),-z.eb}},N:function(a,b){W=b;try{var c=X(),d=X(),e=Kc[c];if(!e)return 0;
|
||||||
|
if(d===e.uc){var g=Q[e.fd],k=e.flags,m=new Uint8Array(F.subarray(c,c+d));g&&g.cb.Ab&&g.cb.Ab(g,m,0,d,k);Kc[c]=null;e.Db&&ha(e.vc)}return 0}catch(y){return"undefined"!==typeof V&&y instanceof K||B(y),-y.eb}},M:function(a,b){W=b;try{var c=X(),d=X(),e=Q[c];if(!e)throw new K(9);ka(e.node,d);return 0}catch(g){return"undefined"!==typeof V&&g instanceof K||B(g),-g.eb}},L:Da,K:function(a,b,c){F.set(F.subarray(b,b+c),a)},J:Ea,r:Mc,q:function(a){var b=Date.now();D[a>>2]=b/1E3|0;D[a+4>>2]=b%1E3*1E3|0;return 0},
|
||||||
|
I:function(a){return Math.log(a)/Math.LN10},p:function(){B("trap!")},H:function(a){Nc();a=new Date(1E3*D[a>>2]);D[15056]=a.getSeconds();D[15057]=a.getMinutes();D[15058]=a.getHours();D[15059]=a.getDate();D[15060]=a.getMonth();D[15061]=a.getFullYear()-1900;D[15062]=a.getDay();var b=new Date(a.getFullYear(),0,1);D[15063]=(a.getTime()-b.getTime())/864E5|0;D[15065]=-(60*a.getTimezoneOffset());var c=(new Date(2E3,6,1)).getTimezoneOffset();b=b.getTimezoneOffset();a=(c!=b&&a.getTimezoneOffset()==Math.min(b,
|
||||||
|
c))|0;D[15064]=a;a=D[Rc()+(a?4:0)>>2];D[15066]=a;return 60224},G:function(a,b){var c=D[a>>2];a=D[a+4>>2];0!==b&&(D[b>>2]=0,D[b+4>>2]=0);return Sc(1E6*c+a/1E3)},F:function(a){switch(a){case 30:return 16384;case 85:return 131068;case 132:case 133:case 12:case 137:case 138:case 15:case 235:case 16:case 17:case 18:case 19:case 20:case 149:case 13:case 10:case 236:case 153:case 9:case 21:case 22:case 159:case 154:case 14:case 77:case 78:case 139:case 80:case 81:case 82:case 68:case 67:case 164:case 11:case 29:case 47:case 48:case 95:case 52:case 51:case 46:return 200809;
|
||||||
|
case 79:return 0;case 27:case 246:case 127:case 128:case 23:case 24:case 160:case 161:case 181:case 182:case 242:case 183:case 184:case 243:case 244:case 245:case 165:case 178:case 179:case 49:case 50:case 168:case 169:case 175:case 170:case 171:case 172:case 97:case 76:case 32:case 173:case 35:return-1;case 176:case 177:case 7:case 155:case 8:case 157:case 125:case 126:case 92:case 93:case 129:case 130:case 131:case 94:case 91:return 1;case 74:case 60:case 69:case 70:case 4:return 1024;case 31:case 42:case 72:return 32;
|
||||||
|
case 87:case 26:case 33:return 2147483647;case 34:case 1:return 47839;case 38:case 36:return 99;case 43:case 37:return 2048;case 0:return 2097152;case 3:return 65536;case 28:return 32768;case 44:return 32767;case 75:return 16384;case 39:return 1E3;case 89:return 700;case 71:return 256;case 40:return 255;case 2:return 100;case 180:return 64;case 25:return 20;case 5:return 16;case 6:return 6;case 73:return 4;case 84:return"object"===typeof navigator?navigator.hardwareConcurrency||1:1}pb(22);return-1},
|
||||||
|
E:function(a){var b=Date.now()/1E3|0;a&&(D[a>>2]=b);return b},D:function(a,b){if(b){var c=1E3*D[b+8>>2];c+=D[b+12>>2]/1E3}else c=Date.now();a=G(a);try{b=c;var d=T(a,{qb:!0}).node;d.ab.hb(d,{timestamp:Math.max(b,c)});return 0}catch(e){a=e;if(!(a instanceof K)){a+=" : ";a:{d=Error();if(!d.stack){try{throw Error(0);}catch(g){d=g}if(!d.stack){d="(no stack trace available)";break a}}d=d.stack.toString()}f.extraStackTrace&&(d+="\n"+f.extraStackTrace());d=Va(d);throw a+d;}pb(a.eb);return-1}},C:function(){B("OOM")},
|
||||||
|
a:Ca},buffer);f.asm=Vc;f._RegisterExtensionFunctions=function(){return f.asm.ha.apply(null,arguments)};var nb=f.___emscripten_environ_constructor=function(){return f.asm.ia.apply(null,arguments)};f.___errno_location=function(){return f.asm.ja.apply(null,arguments)};
|
||||||
|
var Qc=f.__get_daylight=function(){return f.asm.ka.apply(null,arguments)},Pc=f.__get_timezone=function(){return f.asm.la.apply(null,arguments)},Rc=f.__get_tzname=function(){return f.asm.ma.apply(null,arguments)},ha=f._free=function(){return f.asm.na.apply(null,arguments)},Ta=f._malloc=function(){return f.asm.oa.apply(null,arguments)},Tc=f._memalign=function(){return f.asm.pa.apply(null,arguments)},Uc=f._memset=function(){return f.asm.qa.apply(null,arguments)};
|
||||||
|
f._sqlite3_bind_blob=function(){return f.asm.ra.apply(null,arguments)};f._sqlite3_bind_double=function(){return f.asm.sa.apply(null,arguments)};f._sqlite3_bind_int=function(){return f.asm.ta.apply(null,arguments)};f._sqlite3_bind_parameter_index=function(){return f.asm.ua.apply(null,arguments)};f._sqlite3_bind_text=function(){return f.asm.va.apply(null,arguments)};f._sqlite3_changes=function(){return f.asm.wa.apply(null,arguments)};f._sqlite3_clear_bindings=function(){return f.asm.xa.apply(null,arguments)};
|
||||||
|
f._sqlite3_close_v2=function(){return f.asm.ya.apply(null,arguments)};f._sqlite3_column_blob=function(){return f.asm.za.apply(null,arguments)};f._sqlite3_column_bytes=function(){return f.asm.Aa.apply(null,arguments)};f._sqlite3_column_double=function(){return f.asm.Ba.apply(null,arguments)};f._sqlite3_column_name=function(){return f.asm.Ca.apply(null,arguments)};f._sqlite3_column_text=function(){return f.asm.Da.apply(null,arguments)};f._sqlite3_column_type=function(){return f.asm.Ea.apply(null,arguments)};
|
||||||
|
f._sqlite3_create_function_v2=function(){return f.asm.Fa.apply(null,arguments)};f._sqlite3_data_count=function(){return f.asm.Ga.apply(null,arguments)};f._sqlite3_errmsg=function(){return f.asm.Ha.apply(null,arguments)};f._sqlite3_exec=function(){return f.asm.Ia.apply(null,arguments)};f._sqlite3_finalize=function(){return f.asm.Ja.apply(null,arguments)};f._sqlite3_free=function(){return f.asm.Ka.apply(null,arguments)};f._sqlite3_open=function(){return f.asm.La.apply(null,arguments)};
|
||||||
|
f._sqlite3_prepare_v2=function(){return f.asm.Ma.apply(null,arguments)};f._sqlite3_reset=function(){return f.asm.Na.apply(null,arguments)};f._sqlite3_result_double=function(){return f.asm.Oa.apply(null,arguments)};f._sqlite3_result_null=function(){return f.asm.Pa.apply(null,arguments)};f._sqlite3_result_text=function(){return f.asm.Qa.apply(null,arguments)};f._sqlite3_step=function(){return f.asm.Ra.apply(null,arguments)};f._sqlite3_value_blob=function(){return f.asm.Sa.apply(null,arguments)};
|
||||||
|
f._sqlite3_value_bytes=function(){return f.asm.Ta.apply(null,arguments)};f._sqlite3_value_double=function(){return f.asm.Ua.apply(null,arguments)};f._sqlite3_value_int=function(){return f.asm.Va.apply(null,arguments)};f._sqlite3_value_text=function(){return f.asm.Wa.apply(null,arguments)};f._sqlite3_value_type=function(){return f.asm.Xa.apply(null,arguments)};
|
||||||
|
var h=f.stackAlloc=function(){return f.asm.Za.apply(null,arguments)},qa=f.stackRestore=function(){return f.asm._a.apply(null,arguments)},na=f.stackSave=function(){return f.asm.$a.apply(null,arguments)};f.dynCall_vi=function(){return f.asm.Ya.apply(null,arguments)};f.asm=Vc;f.cwrap=function(a,b,c,d){c=c||[];var e=c.every(function(a){return"number"===a});return"string"!==b&&e&&!d?Na(a):function(){return Oa(a,b,c,arguments)}};f.stackSave=na;f.stackRestore=qa;f.stackAlloc=h;
|
||||||
|
function Wc(a){this.name="ExitStatus";this.message="Program terminated with exit("+a+")";this.status=a}Wc.prototype=Error();Wc.prototype.constructor=Wc;gb=function Xc(){f.calledRun||Yc();f.calledRun||(gb=Xc)};
|
||||||
|
function Yc(){function a(){if(!f.calledRun&&(f.calledRun=!0,!Ma)){db||(db=!0,f.noFSInit||Hc||(Hc=!0,Gc(),f.stdin=f.stdin,f.stdout=f.stdout,f.stderr=f.stderr,f.stdin?Ic("stdin",f.stdin):Ac("/dev/tty","/dev/stdin"),f.stdout?Ic("stdout",null,f.stdout):Ac("/dev/tty","/dev/stdout"),f.stderr?Ic("stderr",null,f.stderr):Ac("/dev/tty1","/dev/stderr"),p("/dev/stdin","r"),p("/dev/stdout","w"),p("/dev/stderr","w")),Za(ab));Jb=!1;Za(bb);if(f.onRuntimeInitialized)f.onRuntimeInitialized();if(f.postRun)for("function"==
|
||||||
|
typeof f.postRun&&(f.postRun=[f.postRun]);f.postRun.length;){var a=f.postRun.shift();cb.unshift(a)}Za(cb)}}if(!(0<H)){if(f.preRun)for("function"==typeof f.preRun&&(f.preRun=[f.preRun]);f.preRun.length;)eb();Za($a);0<H||f.calledRun||(f.setStatus?(f.setStatus("Running..."),setTimeout(function(){setTimeout(function(){f.setStatus("")},1);a()},1)):a())}}f.run=Yc;
|
||||||
|
function B(a){if(f.onAbort)f.onAbort(a);void 0!==a?(Aa(a),C(a),a=JSON.stringify(a)):a="";Ma=!0;throw"abort("+a+"). Build with -s ASSERTIONS=1 for more info.";}f.abort=B;if(f.preInit)for("function"==typeof f.preInit&&(f.preInit=[f.preInit]);0<f.preInit.length;)f.preInit.pop()();f.noExitRuntime=!0;Yc();
|
||||||
|
|
||||||
|
|
||||||
|
// The shell-pre.js and emcc-generated code goes above
|
||||||
|
return Module;
|
||||||
|
}); // The end of the promise being returned
|
||||||
|
|
||||||
|
return initSqlJsPromise;
|
||||||
|
} // The end of our initSqlJs function
|
||||||
|
|
||||||
|
// This bit below is copied almost exactly from what you get when you use the MODULARIZE=1 flag with emcc
|
||||||
|
// However, we don't want to use the emcc modularization. See shell-pre.js
|
||||||
|
if (typeof exports === 'object' && typeof module === 'object'){
|
||||||
|
module.exports = initSqlJs;
|
||||||
|
// This will allow the module to be used in ES6 or CommonJS
|
||||||
|
module.exports.default = initSqlJs;
|
||||||
|
}
|
||||||
|
else if (typeof define === 'function' && define['amd']) {
|
||||||
|
define([], function() { return initSqlJs; });
|
||||||
|
}
|
||||||
|
else if (typeof exports === 'object'){
|
||||||
|
exports["Module"] = initSqlJs;
|
||||||
|
}
|
||||||
|
|
BIN
web/sql-wasm.wasm
Normal file
BIN
web/sql-wasm.wasm
Normal file
Binary file not shown.
Loading…
Reference in a new issue