diff --git a/Localizations/en.lproj/Localizable.strings b/Localizations/en.lproj/Localizable.strings
index f7f6054..12c7a4d 100644
--- a/Localizations/en.lproj/Localizable.strings
+++ b/Localizations/en.lproj/Localizable.strings
@@ -253,6 +253,7 @@
"preferences.require-double-tap-to-favorite" = "Require double tap to favorite";
"preferences.show-reblog-and-favorite-counts" = "Show boost and favorite counts";
"preferences.status-word" = "Status word";
+"preferences.display-favorites-as" = "Display favorites as";
"filters.active" = "Active";
"filters.expired" = "Expired";
"filter.add-new" = "Add New Filter";
@@ -279,6 +280,7 @@
"notifications" = "Notifications";
"notifications.reblogged-your-status-%@" = "%@ boosted your status";
"notifications.favourited-your-status-%@" = "%@ favorited your status";
+"notifications.liked-your-status-%@" = "%@ liked your status";
"notifications.followed-you-%@" = "%@ followed you";
"notifications.poll-ended" = "A poll you have voted in has ended";
"notifications.your-poll-ended" = "Your poll has ended";
@@ -311,8 +313,11 @@
"status.delete.confirm.post" = "Are you sure you want to delete this post?";
"status.delete.confirm.toot" = "Are you sure you want to delete this toot?";
"status.delete-and-redraft" = "Delete & re-draft";
-"status.delete-and-redraft.confirm.post" = "Are you sure you want to delete this post and re-draft it? Favorites and boosts will be lost, and replies to the original post will be orphaned.";
-"status.delete-and-redraft.confirm.toot" = "Are you sure you want to delete this toot and re-draft it? Favorites and boosts will be lost, and replies to the original toot will be orphaned.";
+"status.delete-and-redraft.confirm.post-favorites" = "Are you sure you want to delete this post and re-draft it? Favorites and boosts will be lost, and replies to the original post will be orphaned.";
+"status.delete-and-redraft.confirm.toot-favorites" = "Are you sure you want to delete this toot and re-draft it? Favorites and boosts will be lost, and replies to the original toot will be orphaned.";
+"status.delete-and-redraft.confirm.post-likes" = "Are you sure you want to delete this post and re-draft it? Likes and boosts will be lost, and replies to the original post will be orphaned.";
+"status.delete-and-redraft.confirm.toot-likes" = "Are you sure you want to delete this toot and re-draft it? Likes and boosts will be lost, and replies to the original toot will be orphaned.";
+
"status.mute" = "Mute conversation";
"status.new-items.post" = "New posts";
"status.new-items.toot" = "New toots";
@@ -331,6 +336,8 @@
"status.reblog-button.undo.accessibility-label" = "Unboost";
"status.favorite-button.accessibility-label" = "Favorite";
"status.favorite-button.undo.accessibility-label" = "Unfavorite";
+"status.like-button.accessibility-label" = "Like";
+"status.like-button.undo.accessibility-label" = "Unlike";
"status.show-more" = "Show More";
"status.show-more-all-button.accessibilty-label" = "Show more for all";
"status.show-less" = "Show Less";
@@ -356,3 +363,4 @@
"timelines.local" = "Local";
"timelines.federated" = "Federated";
"toot" = "Toot";
+"likes" = "Likes";
diff --git a/Localizations/en.lproj/Localizable.stringsdict b/Localizations/en.lproj/Localizable.stringsdict
index 2cef6d1..05bc76f 100644
--- a/Localizations/en.lproj/Localizable.stringsdict
+++ b/Localizations/en.lproj/Localizable.stringsdict
@@ -66,6 +66,22 @@
%ld Favorites
+ status.likes-count-%ld
+
+ NSStringLocalizedFormatKey
+ %#@likes@
+ likes
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ %ld Like
+ other
+ %ld Likes
+
+
status.replies-count-%ld
NSStringLocalizedFormatKey
diff --git a/ServiceLayer/Sources/ServiceLayer/Utilities/AppPreferences.swift b/ServiceLayer/Sources/ServiceLayer/Utilities/AppPreferences.swift
index 828fa03..c77b6b4 100644
--- a/ServiceLayer/Sources/ServiceLayer/Utilities/AppPreferences.swift
+++ b/ServiceLayer/Sources/ServiceLayer/Utilities/AppPreferences.swift
@@ -32,6 +32,13 @@ public extension AppPreferences {
public var id: String { rawValue }
}
+ enum DisplayFavoritesAs: String, CaseIterable, Identifiable {
+ case favorites
+ case likes
+
+ public var id: String { rawValue }
+ }
+
enum AnimateAvatars: String, CaseIterable, Identifiable {
case everywhere
case profiles
@@ -79,6 +86,18 @@ public extension AppPreferences {
set { self[.statusWord] = newValue.rawValue }
}
+ var displayFavoritesAs: DisplayFavoritesAs {
+ get {
+ if let rawValue = self[.displayFavoritesAs] as String?,
+ let value = DisplayFavoritesAs(rawValue: rawValue) {
+ return value
+ }
+
+ return .favorites
+ }
+ set { self[.displayFavoritesAs] = newValue.rawValue }
+ }
+
var animateAvatars: AnimateAvatars {
get {
if let rawValue = self[.animateAvatars] as String?,
@@ -207,6 +226,7 @@ private extension AppPreferences {
enum Item: String {
case colorScheme
case statusWord
+ case displayFavoritesAs
case requireDoubleTapToReblog
case requireDoubleTapToFavorite
case animateAvatars
diff --git a/View Controllers/TableViewController.swift b/View Controllers/TableViewController.swift
index 0759127..44e2d80 100644
--- a/View Controllers/TableViewController.swift
+++ b/View Controllers/TableViewController.swift
@@ -687,12 +687,17 @@ private extension TableViewController {
let deleteAndRedraftConfirmMessage: String
let deleteConfirmMessage: String
+ let favoritesNoun = viewModel.identityContext.appPreferences.displayFavoritesAs.rawValue
+
switch viewModel.identityContext.appPreferences.statusWord {
case .toot:
- deleteAndRedraftConfirmMessage = NSLocalizedString("status.delete-and-redraft.confirm.toot", comment: "")
+ deleteAndRedraftConfirmMessage = NSLocalizedString(
+ "status.delete-and-redraft.confirm.toot-".appending(favoritesNoun),
+ comment: "")
deleteConfirmMessage = NSLocalizedString("status.delete.confirm.toot", comment: "")
case .post:
- deleteAndRedraftConfirmMessage = NSLocalizedString("status.delete-and-redraft.confirm.post", comment: "")
+ deleteAndRedraftConfirmMessage = NSLocalizedString(
+ "status.delete-and-redraft.confirm.post-".appending(favoritesNoun), comment: "")
deleteConfirmMessage = NSLocalizedString("status.delete.confirm.post", comment: "")
}
diff --git a/Views/SwiftUI/PreferencesView.swift b/Views/SwiftUI/PreferencesView.swift
index 296e58d..24b1dfd 100644
--- a/Views/SwiftUI/PreferencesView.swift
+++ b/Views/SwiftUI/PreferencesView.swift
@@ -101,6 +101,12 @@ struct PreferencesView: View {
Text(option.localizedStringKey).tag(option)
}
}
+ Picker("preferences.display-favorites-as",
+ selection: $identityContext.appPreferences.displayFavoritesAs) {
+ ForEach(AppPreferences.DisplayFavoritesAs.allCases) { option in
+ Text(option.localizedStringKey).tag(option)
+ }
+ }
Toggle("preferences.show-reblog-and-favorite-counts",
isOn: $identityContext.appPreferences.showReblogAndFavoriteCounts)
Toggle("preferences.require-double-tap-to-reblog",
@@ -182,6 +188,17 @@ extension AppPreferences.StatusWord {
}
}
+extension AppPreferences.DisplayFavoritesAs {
+ var localizedStringKey: LocalizedStringKey {
+ switch self {
+ case .favorites:
+ return "favorites"
+ case .likes:
+ return "likes"
+ }
+ }
+}
+
extension AppPreferences.AnimateAvatars {
var localizedStringKey: LocalizedStringKey {
switch self {
diff --git a/Views/UIKit/Content Views/NotificationView.swift b/Views/UIKit/Content Views/NotificationView.swift
index 0e29641..6b854dd 100644
--- a/Views/UIKit/Content Views/NotificationView.swift
+++ b/Views/UIKit/Content Views/NotificationView.swift
@@ -163,9 +163,36 @@ private extension NotificationView {
isAccessibilityElement = true
}
+ var colorForFavoriteView: UIColor {
+ switch notificationConfiguration.viewModel.identityContext.appPreferences.displayFavoritesAs {
+ case .favorites:
+ return .systemYellow
+ case .likes:
+ return .systemRed
+ }
+ }
+
+ var labelForFavoriteView: String {
+ switch notificationConfiguration.viewModel.identityContext.appPreferences.displayFavoritesAs {
+ case .favorites:
+ return "notifications.favourited-your-status-%@"
+ case .likes:
+ return "notifications.liked-your-status-%@"
+ }
+ }
+
+ var imageNameForFavoriteView: String {
+ switch notificationConfiguration.viewModel.identityContext.appPreferences.displayFavoritesAs {
+ case .favorites:
+ return "star.fill"
+ case .likes:
+ return "heart.fill"
+ }
+ }
+
func applyNotificationConfiguration() {
let viewModel = notificationConfiguration.viewModel
-
+ var imageName = viewModel.type.systemImageName
avatarImageView.sd_setImage(with: viewModel.accountViewModel.avatarURL())
switch viewModel.type {
@@ -184,12 +211,13 @@ private extension NotificationView {
identityContext: viewModel.identityContext)
iconImageView.tintColor = .systemGreen
case .favourite:
- typeLabel.attributedText = "notifications.favourited-your-status-%@".localizedBolding(
+ imageName = imageNameForFavoriteView
+ typeLabel.attributedText = labelForFavoriteView.localizedBolding(
displayName: viewModel.accountViewModel.displayName,
emojis: viewModel.accountViewModel.emojis,
label: typeLabel,
identityContext: viewModel.identityContext)
- iconImageView.tintColor = .systemYellow
+ iconImageView.tintColor = colorForFavoriteView
case .poll:
typeLabel.text = NSLocalizedString(
viewModel.accountViewModel.isSelf
@@ -229,7 +257,7 @@ private extension NotificationView {
timeLabel.accessibilityLabel = viewModel.accessibilityTime
iconImageView.image = UIImage(
- systemName: viewModel.type.systemImageName,
+ systemName: imageName,
withConfiguration: UIImage.SymbolConfiguration(scale: .medium))
let accessibilityAttributedLabel = NSMutableAttributedString(string: "")
diff --git a/Views/UIKit/Content Views/StatusView.swift b/Views/UIKit/Content Views/StatusView.swift
index 89a103e..9eb5c34 100644
--- a/Views/UIKit/Content Views/StatusView.swift
+++ b/Views/UIKit/Content Views/StatusView.swift
@@ -451,6 +451,15 @@ private extension StatusView {
.store(in: &cancellables)
}
+ var favoriteCountLabel: String {
+ switch statusConfiguration.viewModel.identityContext.appPreferences.displayFavoritesAs {
+ case .favorites:
+ return "status.favorites-count-%ld"
+ case .likes:
+ return "status.likes-count-%ld"
+ }
+ }
+
func applyStatusConfiguration() {
let viewModel = statusConfiguration.viewModel
let isContextParent = viewModel.configuration.isContextParent
@@ -593,8 +602,9 @@ private extension StatusView {
localizationKey: "status.reblogs-count-%ld",
count: viewModel.reblogsCount)
rebloggedByButton.isHidden = noReblogs
+
favoritedByButton.setAttributedLocalizedTitle(
- localizationKey: "status.favorites-count-%ld",
+ localizationKey: favoriteCountLabel,
count: viewModel.favoritesCount)
favoritedByButton.isHidden = noFavorites
@@ -822,7 +832,7 @@ private extension StatusView {
if statusConfiguration.viewModel.favoritesCount > 0 {
accessibilityAttributedLabel.appendWithSeparator(
String.localizedStringWithFormat(
- NSLocalizedString("status.favorites-count-%ld", comment: ""),
+ NSLocalizedString(favoriteCountLabel, comment: ""),
statusConfiguration.viewModel.favoritesCount))
}
}
@@ -835,10 +845,24 @@ private extension StatusView {
return accessibilityAttributedLabel
}
+ var favoriteIcon: String {
+ switch statusConfiguration.viewModel.identityContext.appPreferences.displayFavoritesAs {
+ case .favorites: return "star"
+ case .likes: return "heart"
+ }
+ }
+
+ var favoritedIcon: String {
+ switch statusConfiguration.viewModel.identityContext.appPreferences.displayFavoritesAs {
+ case .favorites: return "star.fill"
+ case .likes: return "heart.fill"
+ }
+ }
+
func setButtonImages(font: UIFont) {
let visibility = statusConfiguration.viewModel.visibility
let reblogSystemImageName: String
-
+ let isFavorited = statusConfiguration.viewModel.favorited
if statusConfiguration.viewModel.configuration.isContextParent {
reblogSystemImageName = "arrow.2.squarepath"
} else {
@@ -858,7 +882,7 @@ private extension StatusView {
pointSize: font.pointSize,
weight: statusConfiguration.viewModel.reblogged ? .bold : .regular)),
for: .normal)
- favoriteButton.setImage(UIImage(systemName: statusConfiguration.viewModel.favorited ? "star.fill" : "star",
+ favoriteButton.setImage(UIImage(systemName: isFavorited ? favoritedIcon : favoriteIcon,
withConfiguration: UIImage.SymbolConfiguration(pointSize: font.pointSize)),
for: .normal)
shareButton.setImage(UIImage(systemName: "square.and.arrow.up",
@@ -905,17 +929,29 @@ private extension StatusView {
}
func setFavoriteButtonColor(favorited: Bool) {
- let favoriteColor: UIColor = favorited ? .systemYellow : .secondaryLabel
+ var favoriteColor: UIColor
+ var favoriteLabel: String
+ var undoFavoriteLabel: String
+ switch statusConfiguration.viewModel.identityContext.appPreferences.displayFavoritesAs {
+ case .favorites:
+ favoriteColor = favorited ? .systemYellow : .secondaryLabel
+ favoriteLabel = "status.favorite-button.undo.accessibility-label"
+ undoFavoriteLabel = "status.favorite-button.accessibility-label"
+ case .likes:
+ favoriteColor = favorited ? .systemRed : .secondaryLabel
+ favoriteLabel = "status.like-button.undo.accessibility-label"
+ undoFavoriteLabel = "status.like-button.accessibility-label"
+ }
favoriteButton.tintColor = favoriteColor
favoriteButton.setTitleColor(favoriteColor, for: .normal)
if favorited {
favoriteButton.accessibilityLabel =
- NSLocalizedString("status.favorite-button.undo.accessibility-label", comment: "")
+ NSLocalizedString(undoFavoriteLabel, comment: "")
} else {
favoriteButton.accessibilityLabel =
- NSLocalizedString("status.favorite-button.accessibility-label", comment: "")
+ NSLocalizedString(favoriteLabel, comment: "")
}
}
@@ -938,20 +974,47 @@ private extension StatusView {
statusConfiguration.viewModel.toggleReblogged()
}
+ func animateFavorite() {
+ switch statusConfiguration.viewModel.identityContext.appPreferences.displayFavoritesAs {
+
+ case .favorites:
+ self.animateAsFavorite()
+ case .likes:
+ self.animateAsLike()
+ }
+ }
+
+ func animateAsFavorite() {
+ UIViewPropertyAnimator.runningPropertyAnimator(
+ withDuration: .defaultAnimationDuration,
+ delay: 0,
+ options: .curveLinear) {
+ self.setFavoriteButtonColor(favorited: !self.statusConfiguration.viewModel.favorited)
+ self.favoriteButton.imageView?.transform =
+ self.favoriteButton.imageView?.transform.rotated(by: .pi) ?? .identity
+ } completion: { _ in
+ self.favoriteButton.imageView?.transform = .identity
+ }
+ }
+
+ func animateAsLike() {
+ UIViewPropertyAnimator.runningPropertyAnimator(
+ withDuration: .defaultAnimationDuration,
+ delay: 0,
+ options: .curveEaseInOut) {
+ self.setFavoriteButtonColor(favorited: !self.statusConfiguration.viewModel.favorited)
+ self.favoriteButton.imageView?.transform =
+ self.favoriteButton.imageView?.transform.scaledBy(x: 0.7, y: 0.7) ?? .identity
+ } completion: { _ in
+ self.favoriteButton.imageView?.transform = .identity
+ }
+ }
+
func favorite() {
UIImpactFeedbackGenerator(style: .medium).impactOccurred()
if !UIAccessibility.isReduceMotionEnabled {
- UIViewPropertyAnimator.runningPropertyAnimator(
- withDuration: .defaultAnimationDuration,
- delay: 0,
- options: .curveLinear) {
- self.setFavoriteButtonColor(favorited: !self.statusConfiguration.viewModel.favorited)
- self.favoriteButton.imageView?.transform =
- self.favoriteButton.imageView?.transform.rotated(by: .pi) ?? .identity
- } completion: { _ in
- self.favoriteButton.imageView?.transform = .identity
- }
+ self.animateFavorite()
}
statusConfiguration.viewModel.toggleFavorited()