Fix #163 #184
|
@ -98,7 +98,7 @@ class CwtchFfi implements Cwtch {
|
||||||
Map<String, String> envVars = Platform.environment;
|
Map<String, String> envVars = Platform.environment;
|
||||||
String cwtchDir = "";
|
String cwtchDir = "";
|
||||||
if (Platform.isLinux) {
|
if (Platform.isLinux) {
|
||||||
cwtchDir = envVars['CWTCH_HOME'] ?? path.join(envVars['HOME']!, ".cwtch");
|
cwtchDir = envVars['CWTCH_HOME'] ?? path.join(envVars['HOME']!, ".cwtch");
|
||||||
if (await File("linux/tor").exists()) {
|
if (await File("linux/tor").exists()) {
|
||||||
bundledTor = "linux/tor";
|
bundledTor = "linux/tor";
|
||||||
} else if (await File("lib/tor").exists()) {
|
} else if (await File("lib/tor").exists()) {
|
||||||
|
@ -126,7 +126,7 @@ class CwtchFfi implements Cwtch {
|
||||||
print("We couldn't find Tor in the Cwtch app directory, however we can fall back to the Tor Browser binary");
|
print("We couldn't find Tor in the Cwtch app directory, however we can fall back to the Tor Browser binary");
|
||||||
} else {
|
} else {
|
||||||
var splitPath = path.split(dirname(Platform.script.path));
|
var splitPath = path.split(dirname(Platform.script.path));
|
||||||
if (splitPath[0] == "/" && splitPath[1] == "Applications"){
|
if (splitPath[0] == "/" && splitPath[1] == "Applications") {
|
||||||
var appName = splitPath[2];
|
var appName = splitPath[2];
|
||||||
print("We're running in /Applications in a non standard app name: $appName");
|
print("We're running in /Applications in a non standard app name: $appName");
|
||||||
if (await File("/Applications/$appName/Contents/MacOS/Tor/tor.real").exists()) {
|
if (await File("/Applications/$appName/Contents/MacOS/Tor/tor.real").exists()) {
|
||||||
|
@ -138,8 +138,8 @@ class CwtchFfi implements Cwtch {
|
||||||
|
|
||||||
// the first Cwtch MacOS release (1.2) accidently was a dev build
|
// the first Cwtch MacOS release (1.2) accidently was a dev build
|
||||||
// we need to temporarily remedy this for a release or two then delete
|
// we need to temporarily remedy this for a release or two then delete
|
||||||
// if macOs and release build and no profile and is dev profile
|
// if macOs and release build and no profile and is dev profile
|
||||||
// copy dev profile to release profile
|
// copy dev profile to release profile
|
||||||
if (Platform.isMacOS && EnvironmentConfig.BUILD_VER != dev_version) {
|
if (Platform.isMacOS && EnvironmentConfig.BUILD_VER != dev_version) {
|
||||||
var devProfileExists = await Directory(path.join(cwtchDir, "dev", "profiles")).exists();
|
var devProfileExists = await Directory(path.join(cwtchDir, "dev", "profiles")).exists();
|
||||||
var releaseProfileExists = await Directory(path.join(cwtchDir, "profiles")).exists();
|
var releaseProfileExists = await Directory(path.join(cwtchDir, "profiles")).exists();
|
||||||
|
@ -448,7 +448,6 @@ class CwtchFfi implements Cwtch {
|
||||||
malloc.free(u2);
|
malloc.free(u2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
// ignore: non_constant_identifier_names
|
// ignore: non_constant_identifier_names
|
||||||
void UpdateMessageFlags(String profile, String handle, int index, int flags) {
|
void UpdateMessageFlags(String profile, String handle, int index, int flags) {
|
||||||
|
|
|
@ -31,6 +31,7 @@ class AppState extends ChangeNotifier {
|
||||||
String? _selectedProfile;
|
String? _selectedProfile;
|
||||||
String? _selectedConversation;
|
String? _selectedConversation;
|
||||||
int _initialScrollIndex = 0;
|
int _initialScrollIndex = 0;
|
||||||
|
int _hoveredIndex = -1;
|
||||||
int? _selectedIndex;
|
int? _selectedIndex;
|
||||||
bool _unreadMessagesBelow = false;
|
bool _unreadMessagesBelow = false;
|
||||||
|
|
||||||
|
@ -62,6 +63,14 @@ class AppState extends ChangeNotifier {
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Never use this for message lookup - can be a non-indexed value
|
||||||
|
// e.g. -1
|
||||||
|
int get hoveredIndex => _hoveredIndex;
|
||||||
|
set hoveredIndex(int newVal) {
|
||||||
|
this._hoveredIndex = newVal;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
bool get unreadMessagesBelow => _unreadMessagesBelow;
|
bool get unreadMessagesBelow => _unreadMessagesBelow;
|
||||||
set unreadMessagesBelow(bool newVal) {
|
set unreadMessagesBelow(bool newVal) {
|
||||||
this._unreadMessagesBelow = newVal;
|
this._unreadMessagesBelow = newVal;
|
||||||
|
@ -381,21 +390,18 @@ class ContactInfoState extends ChangeNotifier {
|
||||||
String? _server;
|
String? _server;
|
||||||
late bool _archived;
|
late bool _archived;
|
||||||
|
|
||||||
ContactInfoState(
|
ContactInfoState(this.profileOnion, this.onion,
|
||||||
this.profileOnion,
|
{nickname = "",
|
||||||
this.onion, {
|
isGroup = false,
|
||||||
nickname = "",
|
authorization = ContactAuthorization.unknown,
|
||||||
isGroup = false,
|
status = "",
|
||||||
authorization = ContactAuthorization.unknown,
|
imagePath = "",
|
||||||
status = "",
|
savePeerHistory = "DeleteHistoryConfirmed",
|
||||||
imagePath = "",
|
numMessages = 0,
|
||||||
savePeerHistory = "DeleteHistoryConfirmed",
|
numUnread = 0,
|
||||||
numMessages = 0,
|
lastMessageTime,
|
||||||
numUnread = 0,
|
server,
|
||||||
lastMessageTime,
|
archived = false}) {
|
||||||
server,
|
|
||||||
archived = false
|
|
||||||
}) {
|
|
||||||
this._nickname = nickname;
|
this._nickname = nickname;
|
||||||
this._isGroup = isGroup;
|
this._isGroup = isGroup;
|
||||||
this._authorization = authorization;
|
this._authorization = authorization;
|
||||||
|
@ -421,8 +427,8 @@ class ContactInfoState extends ChangeNotifier {
|
||||||
this._archived = archived;
|
this._archived = archived;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
bool get isArchived => this._archived;
|
|
||||||
|
|
||||||
|
bool get isArchived => this._archived;
|
||||||
|
|
||||||
set savePeerHistory(String newVal) {
|
set savePeerHistory(String newVal) {
|
||||||
this._savePeerHistory = newVal;
|
this._savePeerHistory = newVal;
|
||||||
|
|
|
@ -30,6 +30,7 @@ void selectConversation(BuildContext context, String handle) {
|
||||||
Provider.of<AppState>(context, listen: false).initialScrollIndex = initialIndex;
|
Provider.of<AppState>(context, listen: false).initialScrollIndex = initialIndex;
|
||||||
Provider.of<AppState>(context, listen: false).selectedConversation = handle;
|
Provider.of<AppState>(context, listen: false).selectedConversation = handle;
|
||||||
Provider.of<AppState>(context, listen: false).selectedIndex = null;
|
Provider.of<AppState>(context, listen: false).selectedIndex = null;
|
||||||
|
Provider.of<AppState>(context, listen: false).hoveredIndex = -1;
|
||||||
// if in singlepane mode, push to the stack
|
// if in singlepane mode, push to the stack
|
||||||
var isLandscape = Provider.of<AppState>(context, listen: false).isLandscape(context);
|
var isLandscape = Provider.of<AppState>(context, listen: false).isLandscape(context);
|
||||||
if (Provider.of<Settings>(context, listen: false).uiColumns(isLandscape).length == 1) _pushMessageView(context, handle);
|
if (Provider.of<Settings>(context, listen: false).uiColumns(isLandscape).length == 1) _pushMessageView(context, handle);
|
||||||
|
|
|
@ -142,7 +142,7 @@ class _GlobalSettingsViewState extends State<GlobalSettingsView> {
|
||||||
subtitle: Text(AppLocalizations.of(context)!.descriptionStreamerMode),
|
subtitle: Text(AppLocalizations.of(context)!.descriptionStreamerMode),
|
||||||
value: settings.streamerMode,
|
value: settings.streamerMode,
|
||||||
onChanged: (bool value) {
|
onChanged: (bool value) {
|
||||||
settings.setStreamerMode(value);
|
settings.setStreamerMode(value);
|
||||||
// Save Settings...
|
// Save Settings...
|
||||||
saveSettings(context);
|
saveSettings(context);
|
||||||
},
|
},
|
||||||
|
|
|
@ -162,11 +162,12 @@ class _GroupSettingsViewState extends State<GroupSettingsView> {
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
showAlertDialog(context);
|
showAlertDialog(context);
|
||||||
},
|
},
|
||||||
style: ButtonStyle (
|
style: ButtonStyle(backgroundColor: MaterialStateProperty.all(Colors.transparent)),
|
||||||
backgroundColor: MaterialStateProperty.all(Colors.transparent)
|
|
||||||
),
|
|
||||||
icon: Icon(CwtchIcons.leave_group),
|
icon: Icon(CwtchIcons.leave_group),
|
||||||
label: Text(AppLocalizations.of(context)!.leaveGroup, style: TextStyle(decoration: TextDecoration.underline),),
|
label: Text(
|
||||||
|
AppLocalizations.of(context)!.leaveGroup,
|
||||||
|
style: TextStyle(decoration: TextDecoration.underline),
|
||||||
|
),
|
||||||
))
|
))
|
||||||
])
|
])
|
||||||
])
|
])
|
||||||
|
|
|
@ -257,18 +257,27 @@ class _MessageViewState extends State<MessageView> {
|
||||||
color: message.getMetadata().senderHandle != Provider.of<AppState>(context).selectedProfile
|
color: message.getMetadata().senderHandle != Provider.of<AppState>(context).selectedProfile
|
||||||
? Provider.of<Settings>(context).theme.messageFromOtherBackgroundColor()
|
? Provider.of<Settings>(context).theme.messageFromOtherBackgroundColor()
|
||||||
: Provider.of<Settings>(context).theme.messageFromMeBackgroundColor(),
|
: Provider.of<Settings>(context).theme.messageFromMeBackgroundColor(),
|
||||||
child: Wrap(runAlignment: WrapAlignment.spaceEvenly, alignment: WrapAlignment.spaceEvenly, runSpacing: 1.0, crossAxisAlignment: WrapCrossAlignment.center, children: [
|
child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
|
||||||
Center(widthFactor: 1, child: Padding(padding: EdgeInsets.all(10.0), child: Icon(Icons.reply, size: 32))),
|
Stack(children: [
|
||||||
Center(widthFactor: 1.0, child: message.getPreviewWidget(context)),
|
Align(
|
||||||
Center(
|
alignment: Alignment.topRight,
|
||||||
widthFactor: 1.0,
|
child: IconButton(
|
||||||
child: IconButton(
|
icon: Icon(Icons.highlight_remove),
|
||||||
icon: Icon(Icons.highlight_remove),
|
tooltip: AppLocalizations.of(context)!.tooltipRemoveThisQuotedMessage,
|
||||||
tooltip: AppLocalizations.of(context)!.tooltipRemoveThisQuotedMessage,
|
onPressed: () {
|
||||||
onPressed: () {
|
Provider.of<AppState>(context, listen: false).selectedIndex = null;
|
||||||
Provider.of<AppState>(context, listen: false).selectedIndex = null;
|
},
|
||||||
},
|
)),
|
||||||
))
|
Align(
|
||||||
|
alignment: Alignment.topLeft,
|
||||||
|
child: Padding(padding: EdgeInsets.all(2.0), child: Icon(Icons.reply)),
|
||||||
|
)
|
||||||
|
]),
|
||||||
|
Wrap(
|
||||||
|
runAlignment: WrapAlignment.spaceEvenly,
|
||||||
|
alignment: WrapAlignment.center,
|
||||||
|
runSpacing: 1.0,
|
||||||
|
children: [Center(widthFactor: 1.0, child: Padding(padding: EdgeInsets.all(10.0), child: message.getPreviewWidget(context)))]),
|
||||||
]));
|
]));
|
||||||
} else {
|
} else {
|
||||||
return MessageLoadingBubble();
|
return MessageLoadingBubble();
|
||||||
|
|
|
@ -65,11 +65,12 @@ class _ContactRowState extends State<ContactRow> {
|
||||||
child: LinearProgressIndicator(
|
child: LinearProgressIndicator(
|
||||||
color: Provider.of<Settings>(context).theme.defaultButtonActiveColor(),
|
color: Provider.of<Settings>(context).theme.defaultButtonActiveColor(),
|
||||||
)),
|
)),
|
||||||
Visibility(
|
Visibility(
|
||||||
visible: !Provider.of<Settings>(context).streamerMode,
|
visible: !Provider.of<Settings>(context).streamerMode,
|
||||||
child: Text(contact.onion,
|
child: Text(contact.onion,
|
||||||
style: TextStyle(color: contact.isBlocked ? Provider.of<Settings>(context).theme.portraitBlockedTextColor() : Provider.of<Settings>(context).theme.mainTextColor())),
|
style: TextStyle(color: contact.isBlocked ? Provider.of<Settings>(context).theme.portraitBlockedTextColor() : Provider.of<Settings>(context).theme.mainTextColor())),
|
||||||
)],
|
)
|
||||||
|
],
|
||||||
))),
|
))),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(5.0),
|
padding: const EdgeInsets.all(5.0),
|
||||||
|
|
|
@ -56,44 +56,44 @@ class _MessageListState extends State<MessageList> {
|
||||||
Text("")),
|
Text("")),
|
||||||
))),
|
))),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Container(
|
child: Container(
|
||||||
// Only show broken heart is the contact is offline...
|
// Only show broken heart is the contact is offline...
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
image: Provider.of<ContactInfoState>(outerContext).isOnline()
|
image: Provider.of<ContactInfoState>(outerContext).isOnline()
|
||||||
? null
|
? null
|
||||||
: DecorationImage(
|
: DecorationImage(
|
||||||
fit: BoxFit.scaleDown,
|
fit: BoxFit.scaleDown,
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
image: AssetImage("assets/core/negative_heart_512px.png"),
|
image: AssetImage("assets/core/negative_heart_512px.png"),
|
||||||
colorFilter: ColorFilter.mode(Provider.of<Settings>(context).theme.hilightElementTextColor(), BlendMode.srcIn))),
|
colorFilter: ColorFilter.mode(Provider.of<Settings>(context).theme.hilightElementTextColor(), BlendMode.srcIn))),
|
||||||
// Don't load messages for syncing server...
|
// Don't load messages for syncing server...
|
||||||
child: loadMessages
|
child: loadMessages
|
||||||
? ScrollablePositionedList.builder(
|
? ScrollablePositionedList.builder(
|
||||||
itemPositionsListener: widget.scrollListener,
|
itemPositionsListener: widget.scrollListener,
|
||||||
itemScrollController: widget.scrollController,
|
itemScrollController: widget.scrollController,
|
||||||
initialScrollIndex: Provider.of<AppState>(outerContext, listen: false).initialScrollIndex,
|
initialScrollIndex: Provider.of<AppState>(outerContext, listen: false).initialScrollIndex,
|
||||||
itemCount: Provider.of<ContactInfoState>(outerContext).totalMessages,
|
itemCount: Provider.of<ContactInfoState>(outerContext).totalMessages,
|
||||||
reverse: true, // NOTE: There seems to be a bug in flutter that corrects the mouse wheel scroll, but not the drag direction...
|
reverse: true, // NOTE: There seems to be a bug in flutter that corrects the mouse wheel scroll, but not the drag direction...
|
||||||
itemBuilder: (itemBuilderContext, index) {
|
itemBuilder: (itemBuilderContext, index) {
|
||||||
var profileOnion = Provider.of<ProfileInfoState>(outerContext, listen: false).onion;
|
var profileOnion = Provider.of<ProfileInfoState>(outerContext, listen: false).onion;
|
||||||
var contactHandle = Provider.of<ContactInfoState>(outerContext, listen: false).onion;
|
var contactHandle = Provider.of<ContactInfoState>(outerContext, listen: false).onion;
|
||||||
var messageIndex = Provider.of<ContactInfoState>(outerContext).totalMessages - index - 1;
|
var messageIndex = Provider.of<ContactInfoState>(outerContext).totalMessages - index - 1;
|
||||||
|
|
||||||
return FutureBuilder(
|
return FutureBuilder(
|
||||||
future: messageHandler(outerContext, profileOnion, contactHandle, messageIndex),
|
future: messageHandler(outerContext, profileOnion, contactHandle, messageIndex),
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
if (snapshot.hasData) {
|
if (snapshot.hasData) {
|
||||||
var message = snapshot.data as Message;
|
var message = snapshot.data as Message;
|
||||||
// Already includes MessageRow,,
|
// Already includes MessageRow,,
|
||||||
return message.getWidget(context);
|
return message.getWidget(context);
|
||||||
} else {
|
} else {
|
||||||
return MessageLoadingBubble();
|
return MessageLoadingBubble();
|
||||||
}
|
}
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
)
|
);
|
||||||
: null))
|
},
|
||||||
|
)
|
||||||
|
: null))
|
||||||
])));
|
])));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,6 @@ class MessageRow extends StatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class MessageRowState extends State<MessageRow> with SingleTickerProviderStateMixin {
|
class MessageRowState extends State<MessageRow> with SingleTickerProviderStateMixin {
|
||||||
bool showMenu = false;
|
|
||||||
bool showBlockedMessage = false;
|
bool showBlockedMessage = false;
|
||||||
late AnimationController _controller;
|
late AnimationController _controller;
|
||||||
late Animation<Alignment> _animation;
|
late Animation<Alignment> _animation;
|
||||||
|
@ -70,7 +69,7 @@ class MessageRowState extends State<MessageRow> with SingleTickerProviderStateMi
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget wdgIcons = Visibility(
|
Widget wdgIcons = Visibility(
|
||||||
visible: this.showMenu,
|
visible: Provider.of<AppState>(context).hoveredIndex == Provider.of<MessageMetadata>(context).messageIndex,
|
||||||
maintainSize: true,
|
maintainSize: true,
|
||||||
maintainAnimation: true,
|
maintainAnimation: true,
|
||||||
maintainState: true,
|
maintainState: true,
|
||||||
|
@ -164,12 +163,12 @@ class MessageRowState extends State<MessageRow> with SingleTickerProviderStateMi
|
||||||
// For desktop...
|
// For desktop...
|
||||||
onHover: (event) {
|
onHover: (event) {
|
||||||
setState(() {
|
setState(() {
|
||||||
this.showMenu = true;
|
Provider.of<AppState>(context, listen: false).hoveredIndex = Provider.of<MessageMetadata>(context).messageIndex;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
onExit: (event) {
|
onExit: (event) {
|
||||||
setState(() {
|
setState(() {
|
||||||
this.showMenu = false;
|
Provider.of<AppState>(context, listen: false).hoveredIndex = -1;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
|
|
|
@ -47,13 +47,13 @@ class _ProfileRowState extends State<ProfileRow> {
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
Visibility(
|
Visibility(
|
||||||
visible: !Provider.of<Settings>(context).streamerMode,
|
visible: !Provider.of<Settings>(context).streamerMode,
|
||||||
child: ExcludeSemantics(
|
child: ExcludeSemantics(
|
||||||
child: Text(
|
child: Text(
|
||||||
profile.onion,
|
profile.onion,
|
||||||
softWrap: true,
|
softWrap: true,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
)))
|
)))
|
||||||
],
|
],
|
||||||
)),
|
)),
|
||||||
IconButton(
|
IconButton(
|
||||||
|
|
Loading…
Reference in New Issue