model contact uses authorization now; add tooltips to contact pics in chat; dual action: add or goto #105

Merged
sarah merged 2 commits from authTooltip into trunk 2021-07-12 21:52:45 +00:00
15 changed files with 194 additions and 98 deletions

View File

@ -49,8 +49,7 @@ class CwtchNotifier {
nickname: data["nick"],
status: data["status"],
imagePath: data["picture"],
isBlocked: data["authorization"] == "blocked",
isInvitation: data["authorization"] == "unknown",
authorization: stringToContactAuthorization(data["authorization"]),
savePeerHistory: data["saveConversationHistory"] == null ? "DeleteHistoryConfirmed" : data["saveConversationHistory"],
numMessages: int.parse(data["numMessages"]),
numUnread: int.parse(data["unread"]),
@ -69,7 +68,7 @@ class CwtchNotifier {
}
if (profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(data["GroupID"]) == null) {
profileCN.getProfile(data["ProfileOnion"])?.contactList.add(ContactInfoState(data["ProfileOnion"], data["GroupID"],
isInvitation: false, imagePath: data["PicturePath"], nickname: data["GroupName"], status: status, server: data["GroupServer"], isGroup: true, lastMessageTime: DateTime.now()));
authorization: ContactAuthorization.approved, imagePath: data["PicturePath"], nickname: data["GroupName"], status: status, server: data["GroupServer"], isGroup: true, lastMessageTime: DateTime.now()));
profileCN.getProfile(data["ProfileOnion"])?.contactList.updateLastMessageTime(data["GroupID"], DateTime.now());
}
break;
@ -91,8 +90,7 @@ class CwtchNotifier {
contact.status = data["ConnectionState"];
}
if (data["authorization"] != null) {
contact.isInvitation = data["authorization"] == "unknown";
contact.isBlocked = data["authorization"] == "blocked";
contact.authorization = stringToContactAuthorization(data["authorization"]);
}
// contact.[status/isBlocked] might change the list's sort order
profileCN.getProfile(data["ProfileOnion"])?.contactList.resort();
@ -241,7 +239,7 @@ class CwtchNotifier {
if (profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(groupInvite["GroupID"]) == null) {
profileCN.getProfile(data["ProfileOnion"])?.contactList.add(ContactInfoState(data["ProfileOnion"], groupInvite["GroupID"],
isInvitation: false,
authorization: ContactAuthorization.approved,
imagePath: data["PicturePath"],
nickname: groupInvite["GroupName"],
server: groupInvite["ServerHost"],
@ -255,7 +253,7 @@ class CwtchNotifier {
case "AcceptGroupInvite":
EnvironmentConfig.debugLog("accept group invite");
profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(data["GroupID"])!.isInvitation = false;
profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(data["GroupID"])!.authorization = ContactAuthorization.approved;
profileCN.getProfile(data["ProfileOnion"])?.contactList.updateLastMessageTime(data["GroupID"], DateTime.now());
break;
case "ServerStateChange":

View File

@ -1,6 +1,9 @@
{
"@@locale": "de",
"@@last_modified": "2021-07-07T23:42:20+02:00",
"@@last_modified": "2021-07-10T17:32:01+02:00",
dan marked this conversation as resolved Outdated
Outdated
Review

Download these in last updated order so it is more obvious what has changed.

Download these in last updated order so it is more obvious what has changed.
"addContactConfirm": "Add contact %1",
"addContact": "Add contact",
"contactGoto": "Go to conversation with %1",
"settingUIColumnOptionSame": "Same as portrait mode setting",
"settingUIColumnDouble14Ratio": "Double (1:4)",
"settingUIColumnDouble12Ratio": "Double (1:2)",
@ -120,7 +123,7 @@
"password1Label": "Passwort",
"currentPasswordLabel": "aktuelles Passwort",
"yourDisplayName": "Dein Anzeigename",
"profileOnionLabel": "Sende diese Adresse an andere Nutzer, mit denen du dich verbinden möchtest",
"profileOnionLabel": "Senden Sie diese Adresse an Peers, mit denen Sie sich verbinden möchten",
"noPasswordWarning": "Wenn für dieses Konto kein Passwort verwendet wird, bedeutet dies, dass alle lokal gespeicherten Daten nicht verschlüsselt werden.",
"radioNoPassword": "Unverschlüsselt (kein Passwort)",
"radioUsePassword": "Passwort",
@ -132,13 +135,13 @@
"profileName": "Anzeigename",
"editProfileTitle": "Profil bearbeiten",
"addProfileTitle": "Neues Profil hinzufügen",
"deleteBtn": "löschen",
"deleteBtn": "Löschen",
"unblockBtn": "Anderen Nutzer entsperren",
"dontSavePeerHistory": "Verlauf mit anderem Nutzer löschen",
"savePeerHistoryDescription": "Legt fest, ob ein mit dem anderen Nutzer verknüpfter Verlauf gelöscht werden soll oder nicht.",
"savePeerHistory": "Peer-Verlauf speichern",
"blockBtn": "Anderen Nutzer blockieren",
"saveBtn": "speichern",
"saveBtn": "Speichern",
"displayNameLabel": "Angezeigename",
"addressLabel": "Adresse",
"puzzleGameBtn": "Puzzlespiel",

View File

@ -1,6 +1,9 @@
{
"@@locale": "en",
"@@last_modified": "2021-07-07T23:42:20+02:00",
"@@last_modified": "2021-07-10T17:32:01+02:00",
"addContactConfirm": "Add contact %1",
"addContact": "Add contact",
"contactGoto": "Go to conversation with %1",
"settingUIColumnOptionSame": "Same as portrait mode setting",
"settingUIColumnDouble14Ratio": "Double (1:4)",
"settingUIColumnDouble12Ratio": "Double (1:2)",
@ -163,7 +166,7 @@
"update": "Update",
"inviteBtn": "Invite",
"inviteToGroupLabel": "Invite to group",
"groupNameLabel": "Group Name",
"groupNameLabel": "Group name",
"viewServerInfo": "Server Info",
"serverSynced": "Synced",
"serverConnectivityDisconnected": "Server Disconnected",

View File

@ -1,6 +1,9 @@
{
"@@locale": "es",
"@@last_modified": "2021-07-07T23:42:20+02:00",
"@@last_modified": "2021-07-10T17:32:01+02:00",
"addContactConfirm": "Add contact %1",
"addContact": "Add contact",
"contactGoto": "Go to conversation with %1",
"settingUIColumnOptionSame": "Same as portrait mode setting",
"settingUIColumnDouble14Ratio": "Double (1:4)",
"settingUIColumnDouble12Ratio": "Double (1:2)",

View File

@ -1,6 +1,9 @@
{
"@@locale": "fr",
"@@last_modified": "2021-07-07T23:42:20+02:00",
"@@last_modified": "2021-07-10T17:32:01+02:00",
"addContactConfirm": "Add contact %1",
"addContact": "Ajouter le contact",
"contactGoto": "Aller à la conversation avec %1",
"settingUIColumnOptionSame": "Même réglage que pour le mode portrait",
"settingUIColumnDouble14Ratio": "Double (1:4)",
"settingUIColumnDouble12Ratio": "Double (1:2)",
@ -132,7 +135,7 @@
"profileName": "Pseudo",
"editProfileTitle": "Modifier le profil",
"addProfileTitle": "Ajouter un nouveau profil",
"deleteBtn": "Supprimer",
"deleteBtn": "Effacer",
"unblockBtn": "Débloquer le pair",
"dontSavePeerHistory": "Supprimer l'historique des pairs",
"savePeerHistoryDescription": "Détermine s'il faut ou non supprimer tout historique associé au pair.",

View File

@ -1,6 +1,9 @@
{
"@@locale": "it",
"@@last_modified": "2021-07-07T23:42:20+02:00",
"@@last_modified": "2021-07-10T17:32:01+02:00",
"addContactConfirm": "Add contact %1",
"addContact": "Add contact",
"contactGoto": "Go to conversation with %1",
"settingUIColumnOptionSame": "Same as portrait mode setting",
"settingUIColumnDouble14Ratio": "Double (1:4)",
"settingUIColumnDouble12Ratio": "Double (1:2)",
@ -124,7 +127,7 @@
"noPasswordWarning": "Non utilizzare una password su questo account significa che tutti i dati archiviati localmente non verranno criptati",
"radioNoPassword": "Non criptato (senza password)",
"radioUsePassword": "Password",
"copiedToClipboardNotification": "Copiato negli appunti",
"copiedToClipboardNotification": "Copiato negli Appunti",
"copyBtn": "Copia",
"editProfile": "Modifica profilo",
"newProfile": "Nuovo profilo",

View File

@ -1,6 +1,9 @@
{
"@@locale": "pl",
"@@last_modified": "2021-07-07T23:42:20+02:00",
"@@last_modified": "2021-07-10T17:32:01+02:00",
"addContactConfirm": "Add contact %1",
"addContact": "Add contact",
"contactGoto": "Go to conversation with %1",
"settingUIColumnOptionSame": "Same as portrait mode setting",
"settingUIColumnDouble14Ratio": "Double (1:4)",
"settingUIColumnDouble12Ratio": "Double (1:2)",
@ -163,7 +166,7 @@
"update": "Update",
"inviteBtn": "Invite",
"inviteToGroupLabel": "Invite to group",
"groupNameLabel": "Group Name",
"groupNameLabel": "Group name",
"viewServerInfo": "Server Info",
"serverSynced": "Synced",
"serverConnectivityDisconnected": "Server Disconnected",

View File

@ -1,6 +1,9 @@
{
"@@locale": "pt",
"@@last_modified": "2021-07-07T23:42:20+02:00",
"@@last_modified": "2021-07-10T17:32:01+02:00",
"addContactConfirm": "Add contact %1",
"addContact": "Add contact",
"contactGoto": "Go to conversation with %1",
"settingUIColumnOptionSame": "Same as portrait mode setting",
"settingUIColumnDouble14Ratio": "Double (1:4)",
"settingUIColumnDouble12Ratio": "Double (1:2)",
@ -163,7 +166,7 @@
"update": "Update",
"inviteBtn": "Convidar",
"inviteToGroupLabel": "Convidar ao grupo",
"groupNameLabel": "Nome do Grupo",
"groupNameLabel": "Nome do grupo",
"viewServerInfo": "Server Info",
"serverSynced": "Synced",
"serverConnectivityDisconnected": "Server Disconnected",

View File

@ -213,8 +213,7 @@ class ProfileInfoState extends ChangeNotifier {
nickname: contact["name"],
status: contact["status"],
imagePath: contact["picture"],
isBlocked: contact["authorization"] == "blocked",
isInvitation: contact["authorization"] == "unknown",
authorization: stringToContactAuthorization(contact["authorization"]),
savePeerHistory: contact["saveConversationHistory"],
numMessages: contact["numMessages"],
numUnread: contact["numUnread"],
@ -316,8 +315,7 @@ class ProfileInfoState extends ChangeNotifier {
nickname: contact["name"],
status: contact["status"],
imagePath: contact["picture"],
isBlocked: contact["authorization"] == "blocked",
isInvitation: contact["authorization"] == "unknown",
authorization: stringToContactAuthorization(contact["authorization"]),
savePeerHistory: contact["saveConversationHistory"],
numMessages: contact["numMessages"],
numUnread: contact["numUnread"],
@ -331,13 +329,30 @@ class ProfileInfoState extends ChangeNotifier {
}
}
enum ContactAuthorization {
unknown,
approved,
blocked
}
ContactAuthorization stringToContactAuthorization(String authStr) {
switch(authStr) {
case "approved":
return ContactAuthorization.approved;
case "blocked":
return ContactAuthorization.blocked;
default:
return ContactAuthorization.unknown;
}
}
class ContactInfoState extends ChangeNotifier {
final String profileOnion;
final String onion;
late String _nickname;
late bool _isInvitation;
late bool _isBlocked;
late ContactAuthorization _authorization;
late String _status;
late String _imagePath;
late String _savePeerHistory;
@ -355,8 +370,7 @@ class ContactInfoState extends ChangeNotifier {
this.onion, {
nickname = "",
isGroup = false,
isInvitation = false,
isBlocked = false,
authorization = ContactAuthorization.unknown,
status = "",
imagePath = "",
savePeerHistory = "DeleteHistoryConfirmed",
@ -367,8 +381,7 @@ class ContactInfoState extends ChangeNotifier {
}) {
this._nickname = nickname;
this._isGroup = isGroup;
this._isInvitation = isInvitation;
this._isBlocked = isBlocked;
this._authorization = authorization;
this._status = status;
this._imagePath = imagePath;
this._totalMessages = numMessages;
@ -398,15 +411,13 @@ class ContactInfoState extends ChangeNotifier {
notifyListeners();
}
bool get isBlocked => this._isBlocked;
set isBlocked(bool newVal) {
this._isBlocked = newVal;
notifyListeners();
}
bool get isBlocked => this._authorization == ContactAuthorization.blocked;
bool get isInvitation => this._isInvitation;
set isInvitation(bool newVal) {
this._isInvitation = newVal;
bool get isInvitation => this._authorization == ContactAuthorization.unknown;
ContactAuthorization get authorization => this._authorization;
set authorization(ContactAuthorization newAuth) {
this._authorization = newAuth;
notifyListeners();
}

View File

@ -3,6 +3,7 @@ import 'package:flutter/widgets.dart';
import 'package:provider/provider.dart';
import '../main.dart';
import '../model.dart';
import 'messages/invitemessage.dart';
import 'messages/malformedmessage.dart';
import 'messages/quotedmessage.dart';
@ -22,6 +23,8 @@ const TorV3ContactHandleLength = 56;
// Defines the length of a Cwtch v2 Group.
const GroupConversationHandleLength = 32;
abstract class Message {
MessageMetadata getMetadata();
Widget getWidget(BuildContext context);

View File

@ -12,6 +12,8 @@ import 'addcontactview.dart';
import '../model.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'messageview.dart';
class ContactsView extends StatefulWidget {
const ContactsView({Key? key}) : super(key: key);
@ -19,6 +21,38 @@ class ContactsView extends StatefulWidget {
_ContactsViewState createState() => _ContactsViewState();
}
// selectConversation can be called from anywhere to set the active conversation
void selectConversation(BuildContext context, String handle) {
// requery instead of using contactinfostate directly because sometimes listview gets confused about data that resorts
Provider.of<ProfileInfoState>(context, listen: false).contactList.getContact(handle)!.unreadMessages = 0;
// triggers update in Double/TripleColumnView
Provider.of<AppState>(context, listen: false).selectedConversation = handle;
Provider.of<AppState>(context, listen: false).selectedIndex = null;
// if in singlepane mode, push to the stack
var isLandscape = Provider.of<AppState>(context, listen: false).isLandscape(context);
if (Provider.of<Settings>(context, listen: false).uiColumns(isLandscape).length == 1) _pushMessageView(context, handle);
}
void _pushMessageView(BuildContext context, String handle) {
var profileOnion = Provider.of<ProfileInfoState>(context, listen: false).onion;
Navigator.of(context).push(
MaterialPageRoute<void>(
builder: (BuildContext builderContext) {
// assert we have an actual profile...
// We need to listen for updates to the profile in order to update things like invitation message bubbles.
var profile = Provider.of<FlwtchState>(builderContext).profs.getProfile(profileOnion)!;
return MultiProvider(
providers: [
ChangeNotifierProvider.value(value: profile),
ChangeNotifierProvider.value(value: profile.contactList.getContact(handle)!),
],
builder: (context, child) => MessageView(),
);
},
),
);
}
class _ContactsViewState extends State<ContactsView> {
late TextEditingController ctrlrFilter;
bool showSearchBar = false;

View File

@ -114,7 +114,7 @@ class _PeerSettingsViewState extends State<PeerSettingsView> {
value: Provider.of<ContactInfoState>(context).isBlocked,
onChanged: (bool blocked) {
// Save local blocked status
Provider.of<ContactInfoState>(context, listen: false).isBlocked = blocked;
Provider.of<ContactInfoState>(context, listen: false).authorization = ContactAuthorization.blocked;
// Save New peer authorization
var profileOnion = Provider.of<ContactInfoState>(context, listen: false).profileOnion;
@ -216,7 +216,7 @@ class _PeerSettingsViewState extends State<PeerSettingsView> {
showAlertDialog(BuildContext context) {
// set up the buttons
Widget cancelButton = TextButton(
child: Text("Cancel"),
child: Text(AppLocalizations.of(context)!.cancel),
style: ButtonStyle(padding: MaterialStateProperty.all(EdgeInsets.all(20))),
onPressed: () {
Navigator.of(context).pop(); // dismiss dialog

View File

@ -1,3 +1,4 @@
import 'package:cwtch/views/contactsview.dart';
import 'package:flutter/material.dart';
import 'package:flutter/painting.dart';
import 'package:cwtch/views/messageview.dart';
@ -93,39 +94,12 @@ class _ContactRowState extends State<ContactRow> {
),
]),
onTap: () {
setState(() {
// requery instead of using contactinfostate directly because sometimes listview gets confused about data that resorts
Provider.of<ProfileInfoState>(context, listen: false).contactList.getContact(contact.onion)!.unreadMessages = 0;
// triggers update in Double/TripleColumnView
Provider.of<AppState>(context, listen: false).selectedConversation = contact.onion;
Provider.of<AppState>(context, listen: false).selectedIndex = null;
// if in singlepane mode, push to the stack
var isLandscape = Provider.of<AppState>(context, listen: false).isLandscape(context);
if (Provider.of<Settings>(context, listen: false).uiColumns(isLandscape).length == 1) _pushMessageView(contact.onion);
});
selectConversation(context, contact.onion);
},
));
}
void _pushMessageView(String handle) {
var profileOnion = Provider.of<ProfileInfoState>(context, listen: false).onion;
Navigator.of(context).push(
MaterialPageRoute<void>(
builder: (BuildContext builderContext) {
// assert we have an actual profile...
// We need to listen for updates to the profile in order to update things like invitation message bubbles.
var profile = Provider.of<FlwtchState>(builderContext).profs.getProfile(profileOnion)!;
return MultiProvider(
providers: [
ChangeNotifierProvider.value(value: profile),
ChangeNotifierProvider.value(value: profile.contactList.getContact(handle)!),
],
builder: (context, child) => MessageView(),
);
},
),
);
}
void _btnApprove() {
Provider.of<FlwtchState>(context, listen: false)

View File

@ -1,6 +1,7 @@
import 'dart:convert';
import 'package:cwtch/models/message.dart';
import 'package:cwtch/views/contactsview.dart';
import 'package:flutter/material.dart';
import 'package:cwtch/widgets/profileimage.dart';
import 'package:provider/provider.dart';
@ -24,6 +25,17 @@ class MessageRowState extends State<MessageRow> {
@override
Widget build(BuildContext context) {
var fromMe = Provider.of<MessageMetadata>(context).senderHandle == Provider.of<ProfileInfoState>(context).onion;
var isContact = Provider.of<ContactListState>(context).getContact(Provider.of<MessageMetadata>(context).senderHandle) != null;
var senderDisplayStr = "";
if (!fromMe) {
ContactInfoState? contact = Provider.of<ProfileInfoState>(context).contactList.getContact(Provider.of<MessageMetadata>(context).senderHandle);
if (contact != null) {
senderDisplayStr = contact.nickname;
} else {
senderDisplayStr = Provider.of<MessageMetadata>(context).senderHandle;
}
}
Widget wdgIcons = Visibility(
visible: this.showMenu,
@ -45,7 +57,7 @@ class MessageRowState extends State<MessageRow> {
} else {
var contact = Provider.of<ContactInfoState>(context);
Widget wdgPortrait = GestureDetector(
onTap: _btnAdd,
onTap: isContact ? _btnGoto : _btnAdd,
child: Padding(
padding: EdgeInsets.all(4.0),
child: ProfileImage(
@ -54,6 +66,7 @@ class MessageRowState extends State<MessageRow> {
//maskOut: contact.status != "Authenticated",
border: contact.status == "Authenticated" ? Provider.of<Settings>(context).theme.portraitOnlineBorderColor() : Provider.of<Settings>(context).theme.portraitOfflineBorderColor(),
badgeTextColor: Colors.red, badgeColor: Colors.red,
tooltip: isContact ? AppLocalizations.of(context)!.contactGoto.replaceFirst("%1", senderDisplayStr) : AppLocalizations.of(context)!.addContact,
)));
widgetRow = <Widget>[
@ -86,25 +99,62 @@ class MessageRowState extends State<MessageRow> {
child: Padding(padding: EdgeInsets.all(2), child: Row(mainAxisAlignment: fromMe ? MainAxisAlignment.end : MainAxisAlignment.start, children: widgetRow))));
}
void _btnGoto() {
selectConversation(context, Provider.of<MessageMetadata>(context, listen: false).senderHandle);
}
void _btnAdd() {
var sender = Provider.of<MessageMetadata>(context, listen: false).senderHandle;
if (sender == null || sender == "") {
print("sender not yet loaded");
return;
}
var profileOnion = Provider.of<ProfileInfoState>(context, listen: false).onion;
final setPeerAttribute = {
"EventType": "AddContact",
"Data": {"ImportString": sender},
};
final setPeerAttributeJson = jsonEncode(setPeerAttribute);
Provider.of<FlwtchState>(context, listen: false).cwtch.SendProfileEvent(profileOnion, setPeerAttributeJson);
final snackBar = SnackBar(
content: Text(AppLocalizations.of(context)!.successfullAddedContact),
duration: Duration(seconds: 2),
showAddContactConfirmAlertDialog(context, profileOnion, sender);
}
showAddContactConfirmAlertDialog(BuildContext context, String profileOnion, String senderOnion) {
// set up the buttons
Widget cancelButton = TextButton(
child: Text(AppLocalizations.of(context)!.cancel),
style: ButtonStyle(padding: MaterialStateProperty.all(EdgeInsets.all(20))),
onPressed: () {
Navigator.of(context).pop(); // dismiss dialog
},
);
Widget continueButton = TextButton(
style: ButtonStyle(padding: MaterialStateProperty.all(EdgeInsets.all(20))),
child: Text(AppLocalizations.of(context)!.addContact),
onPressed: () {
Provider
.of<FlwtchState>(context, listen: false)
.cwtch
Outdated
Review

Use cwtch.ImportBundle(profileOnion, senderOnion) instead of constructing the Event here, as it is more robust.

Use cwtch.ImportBundle(profileOnion, senderOnion) instead of constructing the Event here, as it is more robust.
.ImportBundle(profileOnion, senderOnion);
final snackBar = SnackBar(
content: Text(AppLocalizations.of(context)!.successfullAddedContact),
duration: Duration(seconds: 2),
);
ScaffoldMessenger.of(context).showSnackBar(snackBar);
Navigator.of(context).pop(); // dismiss dialog
},
);
// set up the AlertDialog
AlertDialog alert = AlertDialog(
title: Text(AppLocalizations.of(context)!.addContactConfirm.replaceFirst("%1", senderOnion)),
actions: [
cancelButton,
continueButton,
],
);
// show the dialog
showDialog(
context: context,
builder: (BuildContext context) {
return alert;
},
);
ScaffoldMessenger.of(context).showSnackBar(snackBar);
}
}

View File

@ -5,7 +5,7 @@ import 'package:provider/provider.dart';
import '../settings.dart';
class ProfileImage extends StatefulWidget {
ProfileImage({required this.imagePath, required this.diameter, required this.border, this.badgeCount = 0, required this.badgeColor, required this.badgeTextColor, this.maskOut = false});
ProfileImage({required this.imagePath, required this.diameter, required this.border, this.badgeCount = 0, required this.badgeColor, required this.badgeTextColor, this.maskOut = false, this.tooltip = ""});
final double diameter;
final String imagePath;
final Color border;
@ -13,6 +13,7 @@ class ProfileImage extends StatefulWidget {
final Color badgeColor;
final Color badgeTextColor;
final bool maskOut;
final String tooltip;
@override
_ProfileImageState createState() => _ProfileImageState();
@ -21,6 +22,21 @@ class ProfileImage extends StatefulWidget {
class _ProfileImageState extends State<ProfileImage> {
@override
Widget build(BuildContext context) {
var image = Image(
image: AssetImage("assets/" + widget.imagePath),
filterQuality: FilterQuality.medium,
// We need some theme specific blending here...we might want to consider making this a theme level attribute
colorBlendMode: !widget.maskOut
? Provider.of<Settings>(context).theme.identifier() == "dark"
? BlendMode.softLight
: BlendMode.darken
: BlendMode.srcOut,
color: Provider.of<Settings>(context).theme.backgroundHilightElementColor(),
isAntiAlias: true,
width: widget.diameter,
height: widget.diameter,
);
return RepaintBoundary(
child: Stack(children: [
ClipOval(
@ -33,20 +49,9 @@ class _ProfileImageState extends State<ProfileImage> {
padding: const EdgeInsets.all(2.0), //border size
child: ClipOval(
clipBehavior: Clip.antiAlias,
child: Image(
image: AssetImage("assets/" + widget.imagePath),
filterQuality: FilterQuality.medium,
// We need some theme specific blending here...we might want to consider making this a theme level attribute
colorBlendMode: !widget.maskOut
? Provider.of<Settings>(context).theme.identifier() == "dark"
? BlendMode.softLight
: BlendMode.darken
: BlendMode.srcOut,
color: Provider.of<Settings>(context).theme.backgroundHilightElementColor(),
isAntiAlias: true,
width: widget.diameter,
height: widget.diameter,
))))),
child: widget.tooltip == "" ? image : Tooltip(
message: widget.tooltip,
child: image))))),
Visibility(
visible: widget.badgeCount > 0,
child: Positioned(