Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add the ability to specify alt text for image posts #1640

Merged
merged 2 commits into from
Jan 17, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions drift_schemas/drift_schema_v6.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"_meta":{"description":"This file contains a serialized version of schema entities for drift.","version":"1.2.0"},"options":{"store_date_time_values_as_text":false},"entities":[{"id":0,"references":[],"type":"table","data":{"name":"accounts","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"int","nullable":false,"customConstraints":null,"defaultConstraints":"PRIMARY KEY AUTOINCREMENT","dialectAwareDefaultConstraints":{"sqlite":"PRIMARY KEY AUTOINCREMENT"},"default_dart":null,"default_client_dart":null,"dsl_features":["auto-increment"]},{"name":"username","getter_name":"username","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"jwt","getter_name":"jwt","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"instance","getter_name":"instance","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"anonymous","getter_name":"anonymous","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"anonymous\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"anonymous\" IN (0, 1))"},"default_dart":"const Constant(false)","default_client_dart":null,"dsl_features":[]},{"name":"user_id","getter_name":"userId","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"list_index","getter_name":"listIndex","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const Constant(-1)","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":false,"constraints":[]}},{"id":1,"references":[],"type":"table","data":{"name":"favorites","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"int","nullable":false,"customConstraints":null,"defaultConstraints":"PRIMARY KEY AUTOINCREMENT","dialectAwareDefaultConstraints":{"sqlite":"PRIMARY KEY AUTOINCREMENT"},"default_dart":null,"default_client_dart":null,"dsl_features":["auto-increment"]},{"name":"account_id","getter_name":"accountId","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"community_id","getter_name":"communityId","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":false,"constraints":[]}},{"id":2,"references":[],"type":"table","data":{"name":"local_subscriptions","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"int","nullable":false,"customConstraints":null,"defaultConstraints":"PRIMARY KEY AUTOINCREMENT","dialectAwareDefaultConstraints":{"sqlite":"PRIMARY KEY AUTOINCREMENT"},"default_dart":null,"default_client_dart":null,"dsl_features":["auto-increment"]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"title","getter_name":"title","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"actor_id","getter_name":"actorId","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"icon","getter_name":"icon","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":false,"constraints":[]}},{"id":3,"references":[],"type":"table","data":{"name":"user_labels","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"int","nullable":false,"customConstraints":null,"defaultConstraints":"PRIMARY KEY AUTOINCREMENT","dialectAwareDefaultConstraints":{"sqlite":"PRIMARY KEY AUTOINCREMENT"},"default_dart":null,"default_client_dart":null,"dsl_features":["auto-increment"]},{"name":"username","getter_name":"username","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"label","getter_name":"label","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":false,"constraints":[]}},{"id":4,"references":[],"type":"table","data":{"name":"drafts","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"int","nullable":false,"customConstraints":null,"defaultConstraints":"PRIMARY KEY AUTOINCREMENT","dialectAwareDefaultConstraints":{"sqlite":"PRIMARY KEY AUTOINCREMENT"},"default_dart":null,"default_client_dart":null,"dsl_features":["auto-increment"]},{"name":"draft_type","getter_name":"draftType","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const DraftTypeConverter()","dart_type_name":"DraftType"}},{"name":"existing_id","getter_name":"existingId","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"reply_id","getter_name":"replyId","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"title","getter_name":"title","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"url","getter_name":"url","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"custom_thumbnail","getter_name":"customThumbnail","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"alt_text","getter_name":"altText","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"body","getter_name":"body","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":false,"constraints":[]}}]}
4 changes: 2 additions & 2 deletions lib/account/models/account.dart
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ class Account {
instance: Value(account.instance),
anonymous: Value(account.anonymous),
userId: Value(account.userId),
listIndex: newIndex,
listIndex: Value(newIndex),
),
);

Expand Down Expand Up @@ -85,7 +85,7 @@ class Account {
instance: Value(anonymousInstance.instance),
anonymous: Value(anonymousInstance.anonymous),
userId: Value(anonymousInstance.userId),
listIndex: newIndex,
listIndex: Value(newIndex),
),
);

Expand Down
11 changes: 10 additions & 1 deletion lib/account/models/draft.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ class Draft {
/// The custom thumbnail of the post
final String? customThumbnail;

/// Alternative text for the image
final String? altText;

/// The body of the post/comment
final String? body;

Expand All @@ -38,6 +41,7 @@ class Draft {
this.title,
this.url,
this.customThumbnail,
this.altText,
this.body,
});

Expand All @@ -49,6 +53,7 @@ class Draft {
String? title,
String? url,
String? customThumbnail,
String? altText,
String? body,
}) =>
Draft(
Expand All @@ -59,11 +64,12 @@ class Draft {
title: title ?? this.title,
url: url ?? this.url,
customThumbnail: customThumbnail ?? this.customThumbnail,
altText: altText ?? this.altText,
body: body ?? this.body,
);

/// See whether this draft contains enough info to save for a post
bool get isPostNotEmpty => title?.isNotEmpty == true || url?.isNotEmpty == true || customThumbnail?.isNotEmpty == true || body?.isNotEmpty == true;
bool get isPostNotEmpty => title?.isNotEmpty == true || url?.isNotEmpty == true || customThumbnail?.isNotEmpty == true || altText?.isNotEmpty == true || body?.isNotEmpty == true;

/// See whether this draft contains enough info to save for a comment
bool get isCommentNotEmpty => body?.isNotEmpty == true;
Expand All @@ -86,6 +92,7 @@ class Draft {
title: Value(draft.title),
url: Value(draft.url),
customThumbnail: Value(draft.customThumbnail),
altText: Value(draft.altText),
body: Value(draft.body),
),
);
Expand All @@ -100,6 +107,7 @@ class Draft {
title: Value(draft.title),
url: Value(draft.url),
customThumbnail: Value(draft.customThumbnail),
altText: Value(draft.altText),
body: Value(draft.body),
),
);
Expand Down Expand Up @@ -130,6 +138,7 @@ class Draft {
title: draft.title,
url: draft.url,
customThumbnail: draft.customThumbnail,
altText: draft.altText,
body: draft.body,
);
} catch (e) {
Expand Down
36 changes: 35 additions & 1 deletion lib/community/pages/create_post_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ class CreatePostPage extends StatefulWidget {
final int? communityId;
final CommunityView? communityView;

/// Whether or not to pre-populate the post with the [title], [text], [image], [url], and/or [customThumbnail]
/// Whether or not to pre-populate the post with the [title], [text], [image], [url], [customThumbnail], and/or [altText]
final bool? prePopulated;

/// Used to pre-populate the post title
Expand All @@ -63,6 +63,9 @@ class CreatePostPage extends StatefulWidget {
/// Used to pre-populate the custom thumbnail for the post
final String? customThumbnail;

/// Alternative text for the image
final String? altText;

/// [postView] is passed in when editing an existing post
final PostView? postView;

Expand All @@ -78,6 +81,7 @@ class CreatePostPage extends StatefulWidget {
this.text,
this.url,
this.customThumbnail,
this.altText,
this.prePopulated = false,
this.postView,
this.onPostSuccess,
Expand Down Expand Up @@ -124,6 +128,9 @@ class _CreatePostPageState extends State<CreatePostPage> {
/// The custom thumbnail for this post.
String? customThumbnail;

/// Alternative text for the image
String? altText;

/// The error message for the shared link if available
String? urlError;

Expand All @@ -146,6 +153,7 @@ class _CreatePostPageState extends State<CreatePostPage> {
final TextEditingController _titleTextController = TextEditingController();
final TextEditingController _urlTextController = TextEditingController();
final TextEditingController _customThumbnailTextController = TextEditingController();
final TextEditingController _altTextTextController = TextEditingController();

/// The focus node for the body. This is used to keep track of the position of the cursor when toggling preview
final FocusNode _bodyFocusNode = FocusNode();
Expand Down Expand Up @@ -182,12 +190,19 @@ class _CreatePostPageState extends State<CreatePostPage> {
debounce(const Duration(milliseconds: 1000), _updatePreview, [customThumbnail]);
});

_altTextTextController.addListener(() {
altText = _altTextTextController.text;
_validateSubmission();
debounce(const Duration(milliseconds: 1000), _updatePreview, [altText]);
});

// Logic for pre-populating the post with the given fields
if (widget.prePopulated == true) {
_titleTextController.text = widget.title ?? '';
_bodyTextController.text = widget.text ?? '';
_urlTextController.text = widget.url ?? '';
_customThumbnailTextController.text = widget.customThumbnail ?? '';
_altTextTextController.text = widget.altText ?? '';
_getDataFromLink(updateTitleField: _titleTextController.text.isEmpty);

if (widget.image != null) {
Expand All @@ -204,6 +219,7 @@ class _CreatePostPageState extends State<CreatePostPage> {
_titleTextController.text = widget.postView!.post.name;
_urlTextController.text = widget.postView!.post.url ?? '';
_customThumbnailTextController.text = widget.postView!.post.thumbnailUrl ?? '';
_altTextTextController.text = widget.postView!.post.altText ?? '';
_bodyTextController.text = widget.postView!.post.body ?? '';
isNSFW = widget.postView!.post.nsfw;
languageId = widget.postView!.post.languageId;
Expand All @@ -221,6 +237,7 @@ class _CreatePostPageState extends State<CreatePostPage> {
_titleTextController.dispose();
_urlTextController.dispose();
_customThumbnailTextController.dispose();
_altTextTextController.dispose();
_bodyFocusNode.dispose();

FocusManager.instance.primaryFocus?.unfocus();
Expand Down Expand Up @@ -260,6 +277,7 @@ class _CreatePostPageState extends State<CreatePostPage> {
_titleTextController.text = draft.title ?? '';
_urlTextController.text = draft.url ?? '';
_customThumbnailTextController.text = draft.customThumbnail ?? '';
_altTextTextController.text = draft.altText ?? '';
_bodyTextController.text = draft.body ?? '';
}

Expand All @@ -282,6 +300,7 @@ class _CreatePostPageState extends State<CreatePostPage> {
_titleTextController.text = widget.postView?.post.name ?? '';
_urlTextController.text = widget.postView?.post.url ?? '';
_customThumbnailTextController.text = widget.postView?.post.thumbnailUrl ?? '';
_altTextTextController.text = widget.postView?.post.altText ?? '';
_bodyTextController.text = widget.postView?.post.body ?? '';
},
);
Expand All @@ -297,6 +316,7 @@ class _CreatePostPageState extends State<CreatePostPage> {
title: _titleTextController.text,
url: _urlTextController.text,
customThumbnail: _customThumbnailTextController.text,
altText: _altTextTextController.text,
body: _bodyTextController.text,
);
}
Expand All @@ -311,6 +331,7 @@ class _CreatePostPageState extends State<CreatePostPage> {
return draft.title != widget.postView!.post.name ||
draft.url != (widget.postView!.post.url ?? '') ||
draft.customThumbnail != (widget.postView!.post.thumbnailUrl ?? '') ||
draft.altText != (widget.postView!.post.altText ?? '') ||
draft.body != (widget.postView!.post.body ?? '');
}

Expand Down Expand Up @@ -501,6 +522,18 @@ class _CreatePostPageState extends State<CreatePostPage> {
),
),
],
if (LemmyClient.instance.supportsFeature(LemmyFeature.altText) && isImageUrl(_urlTextController.text)) ...[
const SizedBox(height: 10),
TextFormField(
controller: _altTextTextController,
decoration: InputDecoration(
labelText: l10n.altText,
isDense: true,
border: const OutlineInputBorder(),
contentPadding: const EdgeInsets.all(13),
),
),
],
SizedBox(height: url.isNotEmpty ? 10 : 5),
Visibility(
visible: url.isNotEmpty,
Expand Down Expand Up @@ -752,6 +785,7 @@ class _CreatePostPageState extends State<CreatePostPage> {
nsfw: isNSFW,
url: url,
customThumbnail: customThumbnail,
altText: altText,
postIdBeingEdited: widget.postView?.post.id,
languageId: languageId,
);
Expand Down
31 changes: 30 additions & 1 deletion lib/core/database/database.dart
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class AppDatabase extends _$AppDatabase {
);

@override
int get schemaVersion => 5;
int get schemaVersion => 6;

@override
MigrationStrategy get migration => MigrationStrategy(
Expand Down Expand Up @@ -67,6 +67,10 @@ class AppDatabase extends _$AppDatabase {
await m.addColumn(schema.accounts, schema.accounts.listIndex);
await customStatement('UPDATE accounts SET list_index = id');
},
from5To6: (m, schema) async {
// Create the alt_text column on the drafts table
await m.addColumn(schema.drafts, schema.drafts.altText);
},
),
);

Expand All @@ -78,9 +82,34 @@ class AppDatabase extends _$AppDatabase {
await impl.validateDatabaseSchema(this);
await customStatement('PRAGMA foreign_keys = ON;');
},
beforeOpen: (details) async {
if (details.versionBefore != null && details.versionBefore! > details.versionNow) {
await _onDowngrade(this, details.versionBefore!, details.versionNow);
}
},
);
}

Future<void> _onDowngrade(AppDatabase database, int fromVersion, int toVersion) async {
await database.customStatement('PRAGMA foreign_keys = OFF');

int current = fromVersion;
while (current > toVersion) {
int target = current - 1;
await _onDownGradeOneStep(database, current, target);
current = target;
}

await database.customStatement('PRAGMA foreign_keys=ON;');
}

Future<void> _onDownGradeOneStep(AppDatabase database, int fromVersion, int toVersion) async {
if (fromVersion == 6 && toVersion == 5) {
// Drop the alt_text column on the drafts table
await database.customStatement('ALTER TABLE drafts DROP COLUMN alt_text');
}
}

Future<String?> exportDatabase() async {
final Directory dbFolder = await getApplicationDocumentsDirectory();
final File file = File(join(dbFolder.path, 'thunder.sqlite'));
Expand Down
Loading
Loading