Merge branch 'main' of gitlab.com:mysocialportal/relatica

merge-requests/67/merge
Hank Grabowski 2024-06-30 08:53:00 -04:00
commit e8d16b571b
7 zmienionych plików z 296 dodań i 249 usunięć

Wyświetl plik

@ -3,10 +3,13 @@
## Version 0.11.0 (beta) ## Version 0.11.0 (beta)
* Changes * Changes
* Connection request notifications won't have timestamps and can't be marked read. They go away when they are * Connection request notifications won't have timestamps and can't be marked read. They go away
when they are
adjudicated. ([Issue #76](https://gitlab.com/mysocialportal/relatica/-/issues/76)) adjudicated. ([Issue #76](https://gitlab.com/mysocialportal/relatica/-/issues/76))
* Add more explicit text and catch when a user tries to use an email address rather than username@servername if * Add more explicit text and catch when a user tries to use an email address rather than
using username/password login. ([Issue #17](https://gitlab.com/mysocialportal/relatica/-/issues/17)) username@servername if
using username/password
login. ([Issue #17](https://gitlab.com/mysocialportal/relatica/-/issues/17))
* Fixes * Fixes
* Fixes Unlisted posts are showing as and sharing as * Fixes Unlisted posts are showing as and sharing as
private ([Issue #78](https://gitlab.com/mysocialportal/relatica/-/issues/78)) private ([Issue #78](https://gitlab.com/mysocialportal/relatica/-/issues/78))
@ -21,30 +24,41 @@
properly ([Issue #98](https://gitlab.com/mysocialportal/relatica/-/issues/98)) properly ([Issue #98](https://gitlab.com/mysocialportal/relatica/-/issues/98))
* Fixes being able to search Threads and Bluesky profiles and posts on the search * Fixes being able to search Threads and Bluesky profiles and posts on the search
page ([Issue #92](https://gitlab.com/mysocialportal/relatica/-/issues/92)) page ([Issue #92](https://gitlab.com/mysocialportal/relatica/-/issues/92))
* Multiple profiles from the same server now works again. Affected users have to use the new "Clear All" button to * Multiple profiles from the same server now works again. Affected users have to use the new "
Clear All" button to
clear out existing credentials and re-add them all to fix clear out existing credentials and re-add them all to fix
though. ([Feature #72](https://gitlab.com/mysocialportal/relatica/-/issues/72)) though. ([Feature #72](https://gitlab.com/mysocialportal/relatica/-/issues/72))
* Fix empty profiles and/or sometimes lack of bidirectional contact data by always pulling profile data on refresh * Fix empty profiles and/or sometimes lack of bidirectional contact data by always pulling
profile data on refresh
requests and adding explicit redraw of panel after user requests requests and adding explicit redraw of panel after user requests
refresh ([Issue #36](https://gitlab.com/mysocialportal/relatica/-/issues/36), [Issue #62](https://gitlab.com/mysocialportal/relatica/-/issues/62),[Issue #70](https://gitlab.com/mysocialportal/relatica/-/issues/70)) refresh ([Issue #36](https://gitlab.com/mysocialportal/relatica/-/issues/36), [Issue #62](https://gitlab.com/mysocialportal/relatica/-/issues/62),[Issue #70](https://gitlab.com/mysocialportal/relatica/-/issues/70))
* Fix bug where read notifications would never load if there were pending connection requests or unread * Fix bug where read notifications would never load if there were pending connection requests or
unread
DMs ([Issue #101](https://gitlab.com/mysocialportal/relatica/-/issues/101)) DMs ([Issue #101](https://gitlab.com/mysocialportal/relatica/-/issues/101))
* New Features * New Features
* Shows the network of the post/comment ([Feature #82](https://gitlab.com/mysocialportal/relatica/-/issues/82)) * Shows the network of the
post/comment ([Feature #82](https://gitlab.com/mysocialportal/relatica/-/issues/82))
* User configurable ability to limit reacting to, commenting on, or resharing posts by network * User configurable ability to limit reacting to, commenting on, or resharing posts by network
type([Feature #93](https://gitlab.com/mysocialportal/relatica/-/issues/93)) type([Feature #93](https://gitlab.com/mysocialportal/relatica/-/issues/93))
* Notifications are grouped by type, starting with mentions, within the unread and read groupings of the * Notifications are grouped by type, starting with mentions, within the unread and read
groupings of the
notification list. Defaults to on by default but can be toggled off in notification list. Defaults to on by default but can be toggled off in
settings.([Feature #65](https://gitlab.com/mysocialportal/relatica/-/issues/65)) settings.([Feature #65](https://gitlab.com/mysocialportal/relatica/-/issues/65))
* Ability to turn off Spoiler Alert/CWs at the application * Ability to turn off Spoiler Alert/CWs at the application
level. Defaults to on. ([Feature #42](https://gitlab.com/mysocialportal/relatica/-/issues/42)) level. Defaults to
on. ([Feature #42](https://gitlab.com/mysocialportal/relatica/-/issues/42))
* Throws a confirm dialog box up if adding a comment to a post/comment over 30 days * Throws a confirm dialog box up if adding a comment to a post/comment over 30 days
old. ([Feature #58](https://gitlab.com/mysocialportal/relatica/-/issues/58)) old. ([Feature #58](https://gitlab.com/mysocialportal/relatica/-/issues/58))
* Autocomplete now lists hashtags and accounts that are used in a post or post above the rest of the * Autocomplete now lists hashtags and accounts that are used in a post or post above the rest of
the
results. ([Feature #28](https://gitlab.com/mysocialportal/relatica/-/issues/28)) results. ([Feature #28](https://gitlab.com/mysocialportal/relatica/-/issues/28))
* Show delivery data for logged in user's posts and comments (not * Show delivery data for logged in user's posts and comments (not
reshares) ([Feature #66](https://gitlab.com/mysocialportal/relatica/-/issues/66)) reshares) ([Feature #66](https://gitlab.com/mysocialportal/relatica/-/issues/66))
* Add spellcheck highlighting in text fields (iOS and Android
only) ([Feature #39](https://gitlab.com/mysocialportal/relatica/-/issues/39))
* Add tie into suggestions from system password manager (confirmed works on iOS only so
far) ([Feature #14](https://gitlab.com/mysocialportal/relatica/-/issues/14))
## Version 0.10.1 (beta) ## Version 0.10.1 (beta)

Wyświetl plik

@ -67,6 +67,7 @@ class _MediaUploadEditorControlState extends State<MediaUploadEditorControl> {
initialValue: widget.media.description, initialValue: widget.media.description,
onChanged: (value) => widget.media.description = value, onChanged: (value) => widget.media.description = value,
textCapitalization: TextCapitalization.sentences, textCapitalization: TextCapitalization.sentences,
spellCheckConfiguration: const SpellCheckConfiguration(),
maxLines: 5, maxLines: 5,
decoration: InputDecoration( decoration: InputDecoration(
labelText: 'Description/ALT Text', labelText: 'Description/ALT Text',

Wyświetl plik

@ -363,6 +363,7 @@ class _EditorScreenState extends State<EditorScreen> {
textCapitalization: TextCapitalization.sentences, textCapitalization: TextCapitalization.sentences,
maxLines: 10, maxLines: 10,
controller: controller, controller: controller,
spellCheckConfiguration: const SpellCheckConfiguration(),
decoration: InputDecoration( decoration: InputDecoration(
labelText: '$statusType Content', labelText: '$statusType Content',
alignLabelWithHint: true, alignLabelWithHint: true,

Wyświetl plik

@ -107,6 +107,7 @@ class _MessageThreadScreenState extends State<MessageThreadScreen> {
child: TextFormField( child: TextFormField(
controller: textController, controller: textController,
textCapitalization: TextCapitalization.sentences, textCapitalization: TextCapitalization.sentences,
spellCheckConfiguration: const SpellCheckConfiguration(),
maxLines: 4, maxLines: 4,
decoration: InputDecoration( decoration: InputDecoration(
labelText: 'Reply Text', labelText: 'Reply Text',

Wyświetl plik

@ -78,6 +78,7 @@ class MessagesNewThread extends StatelessWidget {
TextFormField( TextFormField(
controller: replyController, controller: replyController,
textCapitalization: TextCapitalization.sentences, textCapitalization: TextCapitalization.sentences,
spellCheckConfiguration: const SpellCheckConfiguration(),
maxLines: 8, maxLines: 8,
decoration: InputDecoration( decoration: InputDecoration(
labelText: 'Reply Text', labelText: 'Reply Text',

Wyświetl plik

@ -140,254 +140,259 @@ class _SignInScreenState extends State<SignInScreen> {
child: Form( child: Form(
key: formKey, key: formKey,
child: Center( child: Center(
child: ListView( child: AutofillGroup(
children: [ child: ListView(
Center( children: [
child: DropdownButton<String>( Center(
value: authType, child: DropdownButton<String>(
items: authTypes value: authType,
.map( items: authTypes
(a) => DropdownMenuItem(value: a, child: Text(a))) .map((a) =>
.toList(), DropdownMenuItem(value: a, child: Text(a)))
onChanged: existingAccount .toList(),
? null onChanged: existingAccount
: (value) { ? null
if (existingAccount) { : (value) {
buildSnackbar(context, if (existingAccount) {
"Can't change the type on an existing account"); buildSnackbar(context,
return; "Can't change the type on an existing account");
} return;
authType = value!; }
setState(() {}); authType = value!;
}), setState(() {});
), }),
const VerticalPadding(),
TextFormField(
autocorrect: false,
readOnly: existingAccount,
autovalidateMode: AutovalidateMode.onUserInteraction,
controller: serverNameController,
validator: (value) =>
isFQDN(value ?? '') ? null : 'Not a valid server name',
decoration: InputDecoration(
hintText: 'Server Name (friendica.example.com)',
border: OutlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context).colorScheme.background,
),
borderRadius: BorderRadius.circular(5.0),
),
labelText: 'Server Name',
),
),
const VerticalPadding(),
if (!showUsernameAndPasswordFields) ...[
Text(
existingAccount
? 'Configured to sign in as user ${existingProfile?.handle}'
: 'Relatica will open the requested Friendica site in a web browser where you will be asked to authorize this client.',
softWrap: true,
), ),
const VerticalPadding(), const VerticalPadding(),
],
if (showUsernameAndPasswordFields) ...[
TextFormField( TextFormField(
autocorrect: false,
readOnly: existingAccount, readOnly: existingAccount,
autovalidateMode: AutovalidateMode.onUserInteraction, autovalidateMode: AutovalidateMode.onUserInteraction,
controller: usernameController, controller: serverNameController,
keyboardType: TextInputType.emailAddress, validator: (value) =>
validator: (value) { isFQDN(value ?? '') ? null : 'Not a valid server name',
if (value == null) {
return null;
}
if (value.contains('@')) {
final properFormat = isEmail(value);
if (!properFormat) {
return 'Not a valid Friendica Account Address';
}
final elements = value.split('@');
if (elements.last != serverNameController.text) {
return 'Server name must match above field.\nUsername should be the *Friendica* username.\nThis is not the email address of the account.';
}
return null;
}
return isAlphanumeric(value.replaceAll('-', ''))
? null
: 'Username should be alpha-numeric';
},
decoration: InputDecoration( decoration: InputDecoration(
prefixIcon: const Icon(Icons.alternate_email), hintText: 'Server Name (friendica.example.com)',
hintText:
'Your username on the server (not email address)',
border: OutlineInputBorder( border: OutlineInputBorder(
borderSide: BorderSide( borderSide: BorderSide(
color: Theme.of(context).colorScheme.background, color: Theme.of(context).colorScheme.background,
), ),
borderRadius: BorderRadius.circular(5.0), borderRadius: BorderRadius.circular(5.0),
), ),
labelText: 'Username', labelText: 'Server Name',
), ),
), ),
const VerticalPadding(), const VerticalPadding(),
TextFormField( if (!showUsernameAndPasswordFields) ...[
readOnly: existingAccount, Text(
obscureText: hidePassword, existingAccount
controller: passwordController, ? 'Configured to sign in as user ${existingProfile?.handle}'
validator: (value) { : 'Relatica will open the requested Friendica site in a web browser where you will be asked to authorize this client.',
if (value == null || value.isEmpty) { softWrap: true,
return 'Password field cannot be empty'; ),
} const VerticalPadding(),
],
if (showUsernameAndPasswordFields) ...[
TextFormField(
readOnly: existingAccount,
autovalidateMode: AutovalidateMode.onUserInteraction,
autofillHints: const [AutofillHints.username],
controller: usernameController,
keyboardType: TextInputType.emailAddress,
validator: (value) {
if (value == null) {
return null;
}
return null; if (value.contains('@')) {
}, final properFormat = isEmail(value);
decoration: InputDecoration( if (!properFormat) {
prefixIcon: const Icon(Icons.password), return 'Not a valid Friendica Account Address';
suffixIcon: IconButton( }
onPressed: () { final elements = value.split('@');
setState(() { if (elements.last != serverNameController.text) {
hidePassword = !hidePassword; return 'Server name must match above field.\nUsername should be the *Friendica* username.\nThis is not the email address of the account.';
}); }
}, return null;
icon: hidePassword }
? const Icon(Icons.remove_red_eye_outlined)
: const Icon(Icons.remove_red_eye), return isAlphanumeric(value.replaceAll('-', ''))
), ? null
hintText: 'Password', : 'Username should be alpha-numeric';
border: OutlineInputBorder( },
borderSide: BorderSide( decoration: InputDecoration(
color: Theme.of(context).colorScheme.background, prefixIcon: const Icon(Icons.alternate_email),
hintText:
'Your username on the server (not email address)',
border: OutlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context).colorScheme.background,
),
borderRadius: BorderRadius.circular(5.0),
), ),
borderRadius: BorderRadius.circular(5.0), labelText: 'Username',
), ),
labelText: 'Password',
), ),
), const VerticalPadding(),
TextFormField(
readOnly: existingAccount,
obscureText: hidePassword,
controller: passwordController,
autofillHints: const [AutofillHints.password],
validator: (value) {
if (value == null || value.isEmpty) {
return 'Password field cannot be empty';
}
return null;
},
decoration: InputDecoration(
prefixIcon: const Icon(Icons.password),
suffixIcon: IconButton(
onPressed: () {
setState(() {
hidePassword = !hidePassword;
});
},
icon: hidePassword
? const Icon(Icons.remove_red_eye_outlined)
: const Icon(Icons.remove_red_eye),
),
hintText: 'Password',
border: OutlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context).colorScheme.background,
),
borderRadius: BorderRadius.circular(5.0),
),
labelText: 'Password',
),
),
const VerticalPadding(),
],
signInButtonEnabled
? ElevatedButton(
onPressed: () async => await _signIn(context),
child: const Text('Signin'),
)
: SizedBox(),
const VerticalPadding(), const VerticalPadding(),
], Text(
signInButtonEnabled 'Logged out:',
? ElevatedButton( style: Theme.of(context).textTheme.headlineSmall,
onPressed: () async => await _signIn(context), ),
child: const Text('Signin'), loggedOutProfiles.isEmpty
) ? const Text(
: SizedBox(), 'No logged out profiles',
const VerticalPadding(), textAlign: TextAlign.center,
Text( )
'Logged out:', : Container(
style: Theme.of(context).textTheme.headlineSmall, decoration: BoxDecoration(
), borderRadius: BorderRadius.circular(10),
loggedOutProfiles.isEmpty border: Border.all(
? const Text( width: 0.5,
'No logged out profiles', )),
textAlign: TextAlign.center, child: Column(
) children: loggedOutProfiles.map((p) {
: Container( return ListTile(
decoration: BoxDecoration( onTap: () {
borderRadius: BorderRadius.circular(10), setCredentials(context, p);
border: Border.all(
width: 0.5,
)),
child: Column(
children: loggedOutProfiles.map((p) {
return ListTile(
onTap: () {
setCredentials(context, p);
setState(() {});
},
title: Text(p.handle),
subtitle: Text(p.credentials is BasicCredentials
? usernamePasswordType
: oauthType),
trailing: ElevatedButton(
onPressed: () async {
final confirm = await showYesNoDialog(context,
'Remove login information from app?');
if (confirm ?? false) {
await service.removeProfile(p);
}
setState(() {}); setState(() {});
}, },
child: const Text('Remove'), title: Text(p.handle),
), subtitle: Text(p.credentials is BasicCredentials
);
}).toList(),
),
),
const VerticalPadding(),
Text(
'Logged in:',
style: Theme.of(context).textTheme.headlineSmall,
),
loggedInProfiles.isEmpty
? const Text(
'No logged in profiles',
textAlign: TextAlign.center,
)
: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
border: Border.all(
width: 0.5,
)),
child: Column(
children: loggedInProfiles.map((p) {
final active = service.loggedIn
? p.id == service.currentProfile.id
: false;
return ListTile(
onTap: () async {
setCredentials(context, p);
setState(() {});
},
title: Text(
p.handle,
style: active
? const TextStyle(
fontWeight: FontWeight.bold,
fontStyle: FontStyle.italic)
: null,
),
subtitle: Text(
p.credentials is BasicCredentials
? usernamePasswordType ? usernamePasswordType
: oauthType, : oauthType),
style: active trailing: ElevatedButton(
? const TextStyle( onPressed: () async {
fontWeight: FontWeight.bold, final confirm = await showYesNoDialog(
fontStyle: FontStyle.italic) context,
: null, 'Remove login information from app?');
), if (confirm ?? false) {
trailing: ElevatedButton( await service.removeProfile(p);
onPressed: () async { }
final confirm = await showYesNoDialog(
context, 'Log out account?');
if (confirm == true) {
await getIt<AccountsService>().signOut(p);
setState(() {}); setState(() {});
} },
}, child: const Text('Remove'),
child: const Text('Sign out'), ),
), );
); }).toList(),
}).toList(), ),
), ),
), const VerticalPadding(),
const VerticalPadding(), Text(
ElevatedButton( 'Logged in:',
onPressed: () async { style: Theme.of(context).textTheme.headlineSmall,
final confirm = await showYesNoDialog(context, ),
'Are you sure you want to logout and delete *all* accounts? This cannot be undone.') ?? loggedInProfiles.isEmpty
false; ? const Text(
print(confirm); 'No logged in profiles',
if (!confirm) { textAlign: TextAlign.center,
return; )
} : Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
border: Border.all(
width: 0.5,
)),
child: Column(
children: loggedInProfiles.map((p) {
final active = service.loggedIn
? p.id == service.currentProfile.id
: false;
return ListTile(
onTap: () async {
setCredentials(context, p);
setState(() {});
},
title: Text(
p.handle,
style: active
? const TextStyle(
fontWeight: FontWeight.bold,
fontStyle: FontStyle.italic)
: null,
),
subtitle: Text(
p.credentials is BasicCredentials
? usernamePasswordType
: oauthType,
style: active
? const TextStyle(
fontWeight: FontWeight.bold,
fontStyle: FontStyle.italic)
: null,
),
trailing: ElevatedButton(
onPressed: () async {
final confirm = await showYesNoDialog(
context, 'Log out account?');
if (confirm == true) {
await getIt<AccountsService>().signOut(p);
setState(() {});
}
},
child: const Text('Sign out'),
),
);
}).toList(),
),
),
const VerticalPadding(),
ElevatedButton(
onPressed: () async {
final confirm = await showYesNoDialog(context,
'Are you sure you want to logout and delete *all* accounts? This cannot be undone.') ??
false;
print(confirm);
if (!confirm) {
return;
}
await getIt<AccountsService>().clearAllProfiles(); await getIt<AccountsService>().clearAllProfiles();
}, },
child: Text('Clear All')), child: Text('Clear All')),
], ],
),
), ),
), ),
), ),

Wyświetl plik

@ -733,6 +733,30 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.8.1" version: "4.8.1"
leak_tracker:
dependency: transitive
description:
name: leak_tracker
sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa"
url: "https://pub.dev"
source: hosted
version: "10.0.0"
leak_tracker_flutter_testing:
dependency: transitive
description:
name: leak_tracker_flutter_testing
sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0
url: "https://pub.dev"
source: hosted
version: "2.0.1"
leak_tracker_testing:
dependency: transitive
description:
name: leak_tracker_testing
sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47
url: "https://pub.dev"
source: hosted
version: "2.0.1"
lints: lints:
dependency: transitive dependency: transitive
description: description:
@ -761,18 +785,18 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: matcher name: matcher
sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.12.16" version: "0.12.16+1"
material_color_utilities: material_color_utilities:
dependency: transitive dependency: transitive
description: description:
name: material_color_utilities name: material_color_utilities
sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.5.0" version: "0.8.0"
media_kit: media_kit:
dependency: "direct main" dependency: "direct main"
description: description:
@ -849,10 +873,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: meta name: meta
sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.10.0" version: "1.11.0"
mime: mime:
dependency: transitive dependency: transitive
description: description:
@ -945,10 +969,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: path name: path
sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.8.3" version: "1.9.0"
path_parsing: path_parsing:
dependency: transitive dependency: transitive
description: description:
@ -1523,6 +1547,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.0.17" version: "2.0.17"
vm_service:
dependency: transitive
description:
name: vm_service
sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957
url: "https://pub.dev"
source: hosted
version: "13.0.0"
volume_controller: volume_controller:
dependency: transitive dependency: transitive
description: description:
@ -1555,14 +1587,6 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.1.0" version: "1.1.0"
web:
dependency: transitive
description:
name: web
sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152
url: "https://pub.dev"
source: hosted
version: "0.3.0"
web_socket_channel: web_socket_channel:
dependency: transitive dependency: transitive
description: description:
@ -1620,5 +1644,5 @@ packages:
source: hosted source: hosted
version: "3.1.2" version: "3.1.2"
sdks: sdks:
dart: ">=3.2.0-194.0.dev <4.0.0" dart: ">=3.2.0-0 <4.0.0"
flutter: ">=3.10.0" flutter: ">=3.10.0"