Add first implementation of reblogs on timeline
This commit is contained in:
parent
15bf208496
commit
d8aa3db3ee
|
@ -12,8 +12,9 @@ extension Client {
|
|||
public func getHomeTimeline(maxId: String? = nil,
|
||||
sinceId: String? = nil,
|
||||
minId: String? = nil,
|
||||
limit: Int = 40) async throws -> [Status] {
|
||||
return try await pixelfedClient.getHomeTimeline(maxId: maxId, sinceId: sinceId, minId: minId, limit: limit)
|
||||
limit: Int = 40,
|
||||
includeReblogs: Bool? = nil) async throws -> [Status] {
|
||||
return try await pixelfedClient.getHomeTimeline(maxId: maxId, sinceId: sinceId, minId: minId, limit: limit, includeReblogs: includeReblogs)
|
||||
}
|
||||
|
||||
public func getStatuses(local: Bool? = nil,
|
||||
|
|
|
@ -45,7 +45,7 @@ public class StatusModel: ObservableObject {
|
|||
// If status has been rebloged we are saving orginal status here.
|
||||
let orginalStatus = status.reblog ?? status
|
||||
|
||||
self.id = orginalStatus.id
|
||||
self.id = status.id
|
||||
self.content = orginalStatus.content
|
||||
self.uri = orginalStatus.uri
|
||||
self.url = orginalStatus.url
|
||||
|
|
|
@ -34,6 +34,7 @@ extension ApplicationSettings {
|
|||
@NSManaged public var showAltIconOnTimeline: Bool
|
||||
@NSManaged public var warnAboutMissingAlt: Bool
|
||||
@NSManaged public var showGridOnUserProfile: Bool
|
||||
@NSManaged public var showReboostedStatuses: Bool
|
||||
|
||||
@NSManaged public var customNavigationMenuItem1: Int32
|
||||
@NSManaged public var customNavigationMenuItem2: Int32
|
||||
|
|
|
@ -59,6 +59,7 @@ class ApplicationSettingsHandler {
|
|||
applicationState.showAltIconOnTimeline = defaultSettings.showAltIconOnTimeline
|
||||
applicationState.warnAboutMissingAlt = defaultSettings.warnAboutMissingAlt
|
||||
applicationState.showGridOnUserProfile = defaultSettings.showGridOnUserProfile
|
||||
applicationState.showReboostedStatuses = defaultSettings.showReboostedStatuses
|
||||
|
||||
if let menuPosition = MenuPosition(rawValue: Int(defaultSettings.menuPosition)) {
|
||||
applicationState.menuPosition = menuPosition
|
||||
|
@ -197,6 +198,12 @@ class ApplicationSettingsHandler {
|
|||
CoreDataHandler.shared.save()
|
||||
}
|
||||
|
||||
func set(showReboostedStatuses: Bool) {
|
||||
let defaultSettings = self.get()
|
||||
defaultSettings.showReboostedStatuses = showReboostedStatuses
|
||||
CoreDataHandler.shared.save()
|
||||
}
|
||||
|
||||
private func createApplicationSettingsEntity(viewContext: NSManagedObjectContext? = nil) -> ApplicationSettings {
|
||||
let context = viewContext ?? CoreDataHandler.shared.container.viewContext
|
||||
return ApplicationSettings(context: context)
|
||||
|
|
|
@ -12,7 +12,8 @@ extension StatusData {
|
|||
if let reblog = status.reblog {
|
||||
self.copyFrom(reblog)
|
||||
|
||||
self.rebloggedStatusId = status.id
|
||||
self.id = status.id
|
||||
self.rebloggedStatusId = reblog.id
|
||||
self.rebloggedAccountAvatar = status.account.avatar
|
||||
self.rebloggedAccountDisplayName = status.account.displayName
|
||||
self.rebloggedAccountId = status.account.id
|
||||
|
|
|
@ -3,6 +3,6 @@
|
|||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>_XCCurrentVersionName</key>
|
||||
<string>Vernissage-014.xcdatamodel</string>
|
||||
<string>Vernissage-015.xcdatamodel</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
@ -0,0 +1,104 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="22222" systemVersion="23A344" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
|
||||
<entity name="AccountData" representedClassName="AccountData" syncable="YES">
|
||||
<attribute name="accessToken" optional="YES" attributeType="String"/>
|
||||
<attribute name="acct" attributeType="String"/>
|
||||
<attribute name="avatar" optional="YES" attributeType="URI"/>
|
||||
<attribute name="avatarData" optional="YES" attributeType="Binary"/>
|
||||
<attribute name="clientId" attributeType="String"/>
|
||||
<attribute name="clientSecret" attributeType="String"/>
|
||||
<attribute name="clientVapidKey" attributeType="String"/>
|
||||
<attribute name="createdAt" attributeType="String"/>
|
||||
<attribute name="displayName" optional="YES" attributeType="String"/>
|
||||
<attribute name="followersCount" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="followingCount" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="header" optional="YES" attributeType="URI"/>
|
||||
<attribute name="id" attributeType="String"/>
|
||||
<attribute name="lastSeenStatusId" optional="YES" attributeType="String"/>
|
||||
<attribute name="locked" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="note" optional="YES" attributeType="String"/>
|
||||
<attribute name="refreshToken" optional="YES" attributeType="String"/>
|
||||
<attribute name="serverUrl" attributeType="URI"/>
|
||||
<attribute name="statusesCount" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="url" optional="YES" attributeType="URI"/>
|
||||
<attribute name="username" attributeType="String"/>
|
||||
<relationship name="statuses" optional="YES" toMany="YES" deletionRule="Cascade" destinationEntity="StatusData" inverseName="pixelfedAccount" inverseEntity="StatusData"/>
|
||||
</entity>
|
||||
<entity name="ApplicationSettings" representedClassName="ApplicationSettings" syncable="YES">
|
||||
<attribute name="activeIcon" attributeType="String" defaultValueString="Default"/>
|
||||
<attribute name="avatarShape" attributeType="Integer 32" defaultValueString="1" usesScalarValueType="YES"/>
|
||||
<attribute name="currentAccount" optional="YES" attributeType="String"/>
|
||||
<attribute name="customNavigationMenuItem1" attributeType="Integer 32" defaultValueString="1" usesScalarValueType="YES"/>
|
||||
<attribute name="customNavigationMenuItem2" attributeType="Integer 32" defaultValueString="2" usesScalarValueType="YES"/>
|
||||
<attribute name="customNavigationMenuItem3" attributeType="Integer 32" defaultValueString="5" usesScalarValueType="YES"/>
|
||||
<attribute name="hapticAnimationEnabled" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
|
||||
<attribute name="hapticButtonPressEnabled" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
|
||||
<attribute name="hapticNotificationEnabled" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
|
||||
<attribute name="hapticRefreshEnabled" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
|
||||
<attribute name="hapticTabSelectionEnabled" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
|
||||
<attribute name="lastRefreshTokens" attributeType="Date" defaultDateTimeInterval="694256400" usesScalarValueType="NO"/>
|
||||
<attribute name="menuPosition" attributeType="Integer 32" defaultValueString="1" usesScalarValueType="YES"/>
|
||||
<attribute name="showAltIconOnTimeline" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="showAvatarsOnTimeline" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="showFavouritesOnTimeline" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="showGridOnUserProfile" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="showPhotoDescription" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="showReboostedStatuses" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="showSensitive" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="theme" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="tintColor" attributeType="Integer 32" defaultValueString="2" usesScalarValueType="YES"/>
|
||||
<attribute name="warnAboutMissingAlt" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
|
||||
</entity>
|
||||
<entity name="AttachmentData" representedClassName="AttachmentData" syncable="YES">
|
||||
<attribute name="blurhash" optional="YES" attributeType="String"/>
|
||||
<attribute name="data" optional="YES" attributeType="Binary" allowsExternalBinaryDataStorage="YES"/>
|
||||
<attribute name="exifCamera" optional="YES" attributeType="String"/>
|
||||
<attribute name="exifCreatedDate" optional="YES" attributeType="String"/>
|
||||
<attribute name="exifExposure" optional="YES" attributeType="String"/>
|
||||
<attribute name="exifLens" optional="YES" attributeType="String"/>
|
||||
<attribute name="id" attributeType="String"/>
|
||||
<attribute name="metaImageHeight" optional="YES" attributeType="Integer 32" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="metaImageWidth" optional="YES" attributeType="Integer 32" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="order" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="previewUrl" optional="YES" attributeType="URI"/>
|
||||
<attribute name="remoteUrl" optional="YES" attributeType="URI"/>
|
||||
<attribute name="statusId" attributeType="String"/>
|
||||
<attribute name="text" optional="YES" attributeType="String"/>
|
||||
<attribute name="type" attributeType="String"/>
|
||||
<attribute name="url" attributeType="URI"/>
|
||||
<relationship name="statusRelation" maxCount="1" deletionRule="Nullify" destinationEntity="StatusData" inverseName="attachmentsRelation" inverseEntity="StatusData"/>
|
||||
</entity>
|
||||
<entity name="StatusData" representedClassName="StatusData" syncable="YES">
|
||||
<attribute name="accountAvatar" optional="YES" attributeType="URI"/>
|
||||
<attribute name="accountDisplayName" optional="YES" attributeType="String"/>
|
||||
<attribute name="accountId" attributeType="String"/>
|
||||
<attribute name="accountUsername" optional="YES" attributeType="String"/>
|
||||
<attribute name="applicationName" optional="YES" attributeType="String"/>
|
||||
<attribute name="applicationWebsite" optional="YES" attributeType="URI"/>
|
||||
<attribute name="bookmarked" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="content" attributeType="String"/>
|
||||
<attribute name="createdAt" attributeType="String"/>
|
||||
<attribute name="favourited" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="favouritesCount" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="id" attributeType="String"/>
|
||||
<attribute name="inReplyToAccount" optional="YES" attributeType="String"/>
|
||||
<attribute name="inReplyToId" optional="YES" attributeType="String"/>
|
||||
<attribute name="muted" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="pinned" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="reblogged" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="rebloggedAccountAvatar" optional="YES" attributeType="URI"/>
|
||||
<attribute name="rebloggedAccountDisplayName" optional="YES" attributeType="String"/>
|
||||
<attribute name="rebloggedAccountId" optional="YES" attributeType="String"/>
|
||||
<attribute name="rebloggedAccountUsername" optional="YES" attributeType="String"/>
|
||||
<attribute name="rebloggedStatusId" optional="YES" attributeType="String"/>
|
||||
<attribute name="reblogsCount" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="repliesCount" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="sensitive" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="spoilerText" optional="YES" attributeType="String"/>
|
||||
<attribute name="uri" attributeType="String"/>
|
||||
<attribute name="url" optional="YES" attributeType="URI"/>
|
||||
<attribute name="visibility" attributeType="String"/>
|
||||
<relationship name="attachmentsRelation" optional="YES" toMany="YES" deletionRule="Cascade" destinationEntity="AttachmentData" inverseName="statusRelation" inverseEntity="AttachmentData"/>
|
||||
<relationship name="pixelfedAccount" maxCount="1" deletionRule="Nullify" destinationEntity="AccountData" inverseName="statuses" inverseEntity="AccountData"/>
|
||||
</entity>
|
||||
</model>
|
|
@ -107,6 +107,9 @@ public class ApplicationState: ObservableObject {
|
|||
/// Show grid of photos on user profile.
|
||||
@Published public var showGridOnUserProfile = false
|
||||
|
||||
/// Show reboosted statuses on home timeline.
|
||||
@Published public var showReboostedStatuses = false
|
||||
|
||||
public func changeApplicationState(accountModel: AccountModel, instance: Instance?, lastSeenStatusId: String?) {
|
||||
self.account = accountModel
|
||||
self.lastSeenStatusId = lastSeenStatusId
|
||||
|
|
|
@ -240,6 +240,8 @@
|
|||
"settings.title.showAltTextOnTimeline" = "ALT icon will be displayed on timelines";
|
||||
"settings.title.warnAboutMissingAltTitle" = "Warn of missing ALT text";
|
||||
"settings.title.warnAboutMissingAltDescription" = "A warning about missing ALT texts will be displayed before publishing new post.";
|
||||
"settings.title.enableReboostOnTimeline" = "Show boosted statuses";
|
||||
"settings.title.enableReboostOnTimelineDescription" = "Boosted statuses will be visible on your home timeline.";
|
||||
|
||||
// Mark: Signin view.
|
||||
"signin.navigationBar.title" = "Sign in to Pixelfed";
|
||||
|
|
|
@ -240,6 +240,8 @@
|
|||
"settings.title.showAltTextOnTimeline" = "ALT ikurra (deskribapena edo testu alternatiboa dagoenaren seinale) denbora-lerroan erakutsiko da";
|
||||
"settings.title.warnAboutMissingAltTitle" = "Abisatu ALT ahaztu bazait";
|
||||
"settings.title.warnAboutMissingAltDescription" = "Irudiren batek deskribapenik ez badu, argitaratu baino lehen abisua erakutsiko da.";
|
||||
"settings.title.enableReboostOnTimeline" = "Show boosted statuses";
|
||||
"settings.title.enableReboostOnTimelineDescription" = "Boosted statuses will be visible on your home timeline.";
|
||||
|
||||
// Mark: Signin view.
|
||||
"signin.navigationBar.title" = "Hasi saioa Pixelfed-en";
|
||||
|
|
|
@ -240,6 +240,8 @@
|
|||
"settings.title.showAltTextOnTimeline" = "L'icône ALT sera affichée sur la timeline";
|
||||
"settings.title.warnAboutMissingAltTitle" = "Avertir de l'absence de texte ALT";
|
||||
"settings.title.warnAboutMissingAltDescription" = "Un avertissement concernant les textes ALT manquants sera affiché avant la publication d'un nouveau message.";
|
||||
"settings.title.enableReboostOnTimeline" = "Show boosted statuses";
|
||||
"settings.title.enableReboostOnTimelineDescription" = "Boosted statuses will be visible on your home timeline.";
|
||||
|
||||
// Mark: Signin view.
|
||||
"signin.navigationBar.title" = "Se connecter à Pixelfed";
|
||||
|
|
|
@ -240,6 +240,8 @@
|
|||
"settings.title.showAltTextOnTimeline" = "Ikony ALT będą widonczne na osiach zdjęć";
|
||||
"settings.title.warnAboutMissingAltTitle" = "Ostrzeganie o brakującym tekście ALT";
|
||||
"settings.title.warnAboutMissingAltDescription" = "Ostrzeżenie o brakujących tekstach ALT będzie wyświetlane przed opublikowaniem nowego statusu.";
|
||||
"settings.title.enableReboostOnTimeline" = "Wyświetl podbite statusy";
|
||||
"settings.title.enableReboostOnTimelineDescription" = "Podbite statusy będą widoczne na twojej osi czasu.";
|
||||
|
||||
// Mark: Signin view.
|
||||
"signin.navigationBar.title" = "Zaloguj się do Pixelfed";
|
||||
|
|
|
@ -11,11 +11,12 @@ public extension PixelfedClientAuthenticated {
|
|||
maxId: EntityId? = nil,
|
||||
sinceId: EntityId? = nil,
|
||||
minId: EntityId? = nil,
|
||||
limit: Int? = nil) async throws -> [Status] {
|
||||
limit: Int? = nil,
|
||||
includeReblogs: Bool? = nil) async throws -> [Status] {
|
||||
|
||||
let request = try Self.request(
|
||||
for: baseURL,
|
||||
target: Pixelfed.Timelines.home(maxId, sinceId, minId, limit),
|
||||
target: Pixelfed.Timelines.home(maxId, sinceId, minId, limit, includeReblogs),
|
||||
withBearerToken: token
|
||||
)
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ import Foundation
|
|||
|
||||
extension Pixelfed {
|
||||
public enum Timelines {
|
||||
case home(MaxId?, SinceId?, MinId?, Limit?)
|
||||
case home(MaxId?, SinceId?, MinId?, Limit?, Bool?)
|
||||
case pub(Bool?, Bool?, Bool?, MaxId?, SinceId?, MinId?, Limit?)
|
||||
case tag(String, Bool?, Bool?, Bool?, MaxId?, SinceId?, MinId?, Limit?)
|
||||
}
|
||||
|
@ -43,6 +43,7 @@ extension Pixelfed.Timelines: TargetType {
|
|||
var local: Bool?
|
||||
var remote: Bool?
|
||||
var onlyMedia: Bool?
|
||||
var includeReblogs: Bool?
|
||||
var maxId: MaxId?
|
||||
var sinceId: SinceId?
|
||||
var minId: MinId?
|
||||
|
@ -58,34 +59,46 @@ extension Pixelfed.Timelines: TargetType {
|
|||
sinceId = paramSinceId
|
||||
minId = paramMinId
|
||||
limit = paramLimit
|
||||
case .home(let paramMaxId, let paramSinceId, let paramMinId, let paramLimit):
|
||||
case .home(let paramMaxId, let paramSinceId, let paramMinId, let paramLimit, let paramIncludeReblogs):
|
||||
maxId = paramMaxId
|
||||
sinceId = paramSinceId
|
||||
minId = paramMinId
|
||||
limit = paramLimit
|
||||
includeReblogs = paramIncludeReblogs
|
||||
}
|
||||
|
||||
if let maxId {
|
||||
params.append(("max_id", maxId))
|
||||
}
|
||||
|
||||
if let sinceId {
|
||||
params.append(("since_id", sinceId))
|
||||
}
|
||||
|
||||
if let minId {
|
||||
params.append(("min_id", minId))
|
||||
}
|
||||
|
||||
if let limit {
|
||||
params.append(("limit", "\(limit)"))
|
||||
}
|
||||
|
||||
if let local {
|
||||
params.append(("local", local.asString))
|
||||
}
|
||||
|
||||
if let remote {
|
||||
params.append(("remote", remote.asString))
|
||||
}
|
||||
|
||||
if let onlyMedia {
|
||||
params.append(("only_media", onlyMedia.asString))
|
||||
}
|
||||
|
||||
if let includeReblogs, includeReblogs == true {
|
||||
params.append(("include_reblogs", includeReblogs.asString))
|
||||
}
|
||||
|
||||
return params
|
||||
}
|
||||
|
||||
|
|
|
@ -332,6 +332,7 @@
|
|||
F878842129A4A4E3003CFAD2 /* AppMetadataService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppMetadataService.swift; sourceTree = "<group>"; };
|
||||
F87AEB912986C44E00434FB6 /* AuthorizationSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthorizationSession.swift; sourceTree = "<group>"; };
|
||||
F87AEB962986D16D00434FB6 /* AuthorisationError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthorisationError.swift; sourceTree = "<group>"; };
|
||||
F880EECE2AC70A2B00C09C31 /* Vernissage-015.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "Vernissage-015.xcdatamodel"; sourceTree = "<group>"; };
|
||||
F883401F29B62AE900C3E096 /* SearchView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchView.swift; sourceTree = "<group>"; };
|
||||
F88AB05229B3613900345EDE /* PhotoUrl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhotoUrl.swift; sourceTree = "<group>"; };
|
||||
F88AB05429B3626300345EDE /* ImageGrid.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageGrid.swift; sourceTree = "<group>"; };
|
||||
|
@ -1805,6 +1806,7 @@
|
|||
F88C2476295C37BB0006098B /* Vernissage.xcdatamodeld */ = {
|
||||
isa = XCVersionGroup;
|
||||
children = (
|
||||
F880EECE2AC70A2B00C09C31 /* Vernissage-015.xcdatamodel */,
|
||||
F8206A032A06547600E19412 /* Vernissage-014.xcdatamodel */,
|
||||
F865B4D42A0252FB008ACDFC /* Vernissage-013.xcdatamodel */,
|
||||
F8EF3C8B29FC3A5F00CBFF7C /* Vernissage-012.xcdatamodel */,
|
||||
|
@ -1821,7 +1823,7 @@
|
|||
F8C937A929882CA90004D782 /* Vernissage-001.xcdatamodel */,
|
||||
F88C2477295C37BB0006098B /* Vernissage.xcdatamodel */,
|
||||
);
|
||||
currentVersion = F8206A032A06547600E19412 /* Vernissage-014.xcdatamodel */;
|
||||
currentVersion = F880EECE2AC70A2B00C09C31 /* Vernissage-015.xcdatamodel */;
|
||||
path = Vernissage.xcdatamodeld;
|
||||
sourceTree = "<group>";
|
||||
versionGroupType = wrapper.xcdatamodel;
|
||||
|
|
|
@ -20,7 +20,7 @@ public class HomeTimelineService {
|
|||
private let imagePrefetcher = ImagePrefetcher(destination: .diskCache)
|
||||
|
||||
@MainActor
|
||||
public func loadOnBottom(for account: AccountModel) async throws -> Int {
|
||||
public func loadOnBottom(for account: AccountModel, includeReblogs: Bool) async throws -> Int {
|
||||
// Load data from API and operate on CoreData on background context.
|
||||
let backgroundContext = CoreDataHandler.shared.newBackgroundContext()
|
||||
|
||||
|
@ -32,7 +32,7 @@ public class HomeTimelineService {
|
|||
}
|
||||
|
||||
// Load data on bottom of the list.
|
||||
let allStatusesFromApi = try await self.load(for: account, on: backgroundContext, maxId: oldestStatus.id)
|
||||
let allStatusesFromApi = try await self.load(for: account, includeReblogs: includeReblogs, on: backgroundContext, maxId: oldestStatus.id)
|
||||
|
||||
// Save data into database.
|
||||
CoreDataHandler.shared.save(viewContext: backgroundContext)
|
||||
|
@ -45,7 +45,7 @@ public class HomeTimelineService {
|
|||
}
|
||||
|
||||
@MainActor
|
||||
public func refreshTimeline(for account: AccountModel, updateLastSeenStatus: Bool = false) async throws -> String? {
|
||||
public func refreshTimeline(for account: AccountModel, includeReblogs: Bool, updateLastSeenStatus: Bool = false) async throws -> String? {
|
||||
// Load data from API and operate on CoreData on background context.
|
||||
let backgroundContext = CoreDataHandler.shared.newBackgroundContext()
|
||||
|
||||
|
@ -55,7 +55,7 @@ public class HomeTimelineService {
|
|||
|
||||
// Refresh/load home timeline (refreshing on top downloads always first 40 items).
|
||||
// When Apple introduce good way to show new items without scroll to top then we can change that method.
|
||||
let allStatusesFromApi = try await self.refresh(for: account, on: backgroundContext)
|
||||
let allStatusesFromApi = try await self.refresh(for: account, includeReblogs: includeReblogs, on: backgroundContext)
|
||||
|
||||
// Update last seen status.
|
||||
if let lastSeenStatusId, updateLastSeenStatus == true {
|
||||
|
@ -107,7 +107,7 @@ public class HomeTimelineService {
|
|||
CoreDataHandler.shared.save()
|
||||
}
|
||||
|
||||
public func amountOfNewStatuses(for account: AccountModel) async -> Int {
|
||||
public func amountOfNewStatuses(for account: AccountModel, includeReblogs: Bool) async -> Int {
|
||||
guard let accessToken = account.accessToken else {
|
||||
return 0
|
||||
}
|
||||
|
@ -128,7 +128,7 @@ public class HomeTimelineService {
|
|||
// There can be more then 40 newest statuses, that's why we have to sometimes send more then one request.
|
||||
while true {
|
||||
do {
|
||||
let downloadedStatuses = try await client.getHomeTimeline(minId: newestStatusId, limit: self.defaultAmountOfDownloadedStatuses)
|
||||
let downloadedStatuses = try await client.getHomeTimeline(minId: newestStatusId, limit: self.defaultAmountOfDownloadedStatuses, includeReblogs: includeReblogs)
|
||||
guard let firstStatus = downloadedStatuses.first else {
|
||||
break
|
||||
}
|
||||
|
@ -147,14 +147,14 @@ public class HomeTimelineService {
|
|||
return amountOfStatuses
|
||||
}
|
||||
|
||||
private func refresh(for account: AccountModel, on backgroundContext: NSManagedObjectContext) async throws -> [Status] {
|
||||
private func refresh(for account: AccountModel, includeReblogs: Bool, on backgroundContext: NSManagedObjectContext) async throws -> [Status] {
|
||||
guard let accessToken = account.accessToken else {
|
||||
return []
|
||||
}
|
||||
|
||||
// Retrieve statuses from API.
|
||||
let client = PixelfedClient(baseURL: account.serverUrl).getAuthenticated(token: accessToken)
|
||||
let statuses = try await client.getHomeTimeline(limit: self.defaultAmountOfDownloadedStatuses)
|
||||
let statuses = try await client.getHomeTimeline(limit: self.defaultAmountOfDownloadedStatuses, includeReblogs: includeReblogs)
|
||||
|
||||
// Update all existing statuses in database.
|
||||
for status in statuses {
|
||||
|
@ -207,6 +207,7 @@ public class HomeTimelineService {
|
|||
}
|
||||
|
||||
private func load(for account: AccountModel,
|
||||
includeReblogs: Bool,
|
||||
on backgroundContext: NSManagedObjectContext,
|
||||
minId: String? = nil,
|
||||
maxId: String? = nil
|
||||
|
@ -217,7 +218,7 @@ public class HomeTimelineService {
|
|||
|
||||
// Retrieve statuses from API.
|
||||
let client = PixelfedClient(baseURL: account.serverUrl).getAuthenticated(token: accessToken)
|
||||
let statuses = try await client.getHomeTimeline(maxId: maxId, minId: minId, limit: self.defaultAmountOfDownloadedStatuses)
|
||||
let statuses = try await client.getHomeTimeline(maxId: maxId, minId: minId, limit: self.defaultAmountOfDownloadedStatuses, includeReblogs: includeReblogs)
|
||||
|
||||
// Save statuses in database.
|
||||
try await self.add(statuses, for: account, on: backgroundContext)
|
||||
|
|
|
@ -204,7 +204,8 @@ struct VernissageApp: App {
|
|||
|
||||
private func calculateNewPhotosInBackground() async {
|
||||
if let account = self.applicationState.account {
|
||||
self.applicationState.amountOfNewStatuses = await HomeTimelineService.shared.amountOfNewStatuses(for: account)
|
||||
self.applicationState.amountOfNewStatuses = await HomeTimelineService.shared.amountOfNewStatuses(for: account,
|
||||
includeReblogs: self.applicationState.showReboostedStatuses)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -69,7 +69,7 @@ struct HomeFeedView: View {
|
|||
.task {
|
||||
do {
|
||||
if let account = self.applicationState.account {
|
||||
let newStatusesCount = try await HomeTimelineService.shared.loadOnBottom(for: account)
|
||||
let newStatusesCount = try await HomeTimelineService.shared.loadOnBottom(for: account, includeReblogs: self.applicationState.showReboostedStatuses)
|
||||
if newStatusesCount == 0 {
|
||||
allItemsLoaded = true
|
||||
}
|
||||
|
@ -101,7 +101,7 @@ struct HomeFeedView: View {
|
|||
private func refreshData() async {
|
||||
do {
|
||||
if let account = self.applicationState.account {
|
||||
let lastSeenStatusId = try await HomeTimelineService.shared.refreshTimeline(for: account, updateLastSeenStatus: true)
|
||||
let lastSeenStatusId = try await HomeTimelineService.shared.refreshTimeline(for: account, includeReblogs: self.applicationState.showReboostedStatuses, updateLastSeenStatus: true)
|
||||
|
||||
asyncAfter(0.35) {
|
||||
self.applicationState.lastSeenStatusId = lastSeenStatusId
|
||||
|
@ -125,7 +125,7 @@ struct HomeFeedView: View {
|
|||
}
|
||||
|
||||
if let account = self.applicationState.account {
|
||||
_ = try await HomeTimelineService.shared.refreshTimeline(for: account)
|
||||
_ = try await HomeTimelineService.shared.refreshTimeline(for: account, includeReblogs: self.applicationState.showReboostedStatuses)
|
||||
}
|
||||
|
||||
self.applicationState.amountOfNewStatuses = 0
|
||||
|
|
|
@ -85,6 +85,18 @@ struct MediaSettingsView: View {
|
|||
.onChange(of: self.applicationState.warnAboutMissingAlt) { newValue in
|
||||
ApplicationSettingsHandler.shared.set(warnAboutMissingAlt: newValue)
|
||||
}
|
||||
|
||||
Toggle(isOn: $applicationState.showReboostedStatuses) {
|
||||
VStack(alignment: .leading) {
|
||||
Text("settings.title.enableReboostOnTimeline", comment: "Show boosted statuses")
|
||||
Text("settings.title.enableReboostOnTimelineDescription", comment: "Boosted statuses will be visible on your home timeline.")
|
||||
.font(.footnote)
|
||||
.foregroundColor(.customGrayColor)
|
||||
}
|
||||
}
|
||||
.onChange(of: self.applicationState.showReboostedStatuses) { newValue in
|
||||
ApplicationSettingsHandler.shared.set(showReboostedStatuses: newValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -239,7 +239,8 @@ struct StatusesView: View {
|
|||
maxId: maxId,
|
||||
sinceId: sinceId,
|
||||
minId: minId,
|
||||
limit: self.defaultLimit) ?? []
|
||||
limit: self.defaultLimit,
|
||||
includeReblogs: self.applicationState.showReboostedStatuses) ?? []
|
||||
case .local:
|
||||
return try await self.client.publicTimeline?.getStatuses(
|
||||
local: true,
|
||||
|
|
|
@ -61,7 +61,10 @@ struct ImageRowItem: View {
|
|||
} blurred: {
|
||||
ZStack {
|
||||
BlurredImage(blurhash: attachmentData.blurhash)
|
||||
ImageAvatar(displayName: self.status.accountDisplayName, avatarUrl: self.status.accountAvatar) {
|
||||
ImageAvatar(displayName: self.status.accountDisplayName,
|
||||
avatarUrl: self.status.accountAvatar,
|
||||
rebloggedAccountDisplayName: self.status.rebloggedAccountDisplayName,
|
||||
rebloggedAccountAvatar: self.status.rebloggedAccountAvatar) {
|
||||
self.routerPath.navigate(to: .userProfile(accountId: self.status.accountId,
|
||||
accountDisplayName: self.status.accountDisplayName,
|
||||
accountUserName: self.status.accountUsername))
|
||||
|
@ -141,7 +144,10 @@ struct ImageRowItem: View {
|
|||
ZStack {
|
||||
self.imageView(uiImage: uiImage)
|
||||
|
||||
ImageAvatar(displayName: self.status.accountDisplayName, avatarUrl: self.status.accountAvatar) {
|
||||
ImageAvatar(displayName: self.status.accountDisplayName,
|
||||
avatarUrl: self.status.accountAvatar,
|
||||
rebloggedAccountDisplayName: self.status.rebloggedAccountDisplayName,
|
||||
rebloggedAccountAvatar: self.status.rebloggedAccountAvatar) {
|
||||
self.routerPath.navigate(to: .userProfile(accountId: self.status.accountId,
|
||||
accountDisplayName: self.status.accountDisplayName,
|
||||
accountUserName: self.status.accountUsername))
|
||||
|
@ -155,6 +161,23 @@ struct ImageRowItem: View {
|
|||
FavouriteTouch(showFavouriteAnimation: $showThumbImage)
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
func reblogInformation() -> some View {
|
||||
if let rebloggedAccountAvatar = self.status.rebloggedAccountAvatar,
|
||||
let rebloggedAccountDisplayName = self.status.rebloggedAccountDisplayName {
|
||||
HStack(alignment: .center, spacing: 4) {
|
||||
UserAvatar(accountAvatar: rebloggedAccountAvatar, size: .mini)
|
||||
Text(rebloggedAccountDisplayName)
|
||||
Image("custom.rocket")
|
||||
.padding(.trailing, 8)
|
||||
}
|
||||
.font(.footnote)
|
||||
.foregroundColor(Color.mainTextColor.opacity(0.4))
|
||||
.background(Color.mainTextColor.opacity(0.1))
|
||||
.clipShape(Capsule())
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private func imageView(uiImage: UIImage) -> some View {
|
||||
|
@ -228,7 +251,7 @@ struct ImageRowItem: View {
|
|||
|
||||
private func navigateToStatus() {
|
||||
self.routerPath.navigate(to: .status(
|
||||
id: status.rebloggedStatusId ?? status.id,
|
||||
id: status.id,
|
||||
blurhash: status.attachments().first?.blurhash,
|
||||
highestImageUrl: status.attachments().getHighestImage()?.url,
|
||||
metaImageWidth: status.attachments().first?.metaImageWidth,
|
||||
|
|
|
@ -67,7 +67,9 @@ struct ImageRowItemAsync: View {
|
|||
BlurredImage(blurhash: attachment.blurhash)
|
||||
if self.showAvatar {
|
||||
ImageAvatar(displayName: self.statusViewModel.account.displayNameWithoutEmojis,
|
||||
avatarUrl: self.statusViewModel.account.avatar) {
|
||||
avatarUrl: self.statusViewModel.account.avatar,
|
||||
rebloggedAccountDisplayName: self.statusViewModel.reblogStatus?.account.displayNameWithoutEmojis,
|
||||
rebloggedAccountAvatar: self.statusViewModel.reblogStatus?.account.avatar) {
|
||||
self.routerPath.navigate(to: .userProfile(accountId: self.statusViewModel.account.id,
|
||||
accountDisplayName: self.statusViewModel.account.displayNameWithoutEmojis,
|
||||
accountUserName: self.statusViewModel.account.acct))
|
||||
|
@ -143,7 +145,9 @@ struct ImageRowItemAsync: View {
|
|||
|
||||
if self.showAvatar {
|
||||
ImageAvatar(displayName: self.statusViewModel.account.displayNameWithoutEmojis,
|
||||
avatarUrl: self.statusViewModel.account.avatar) {
|
||||
avatarUrl: self.statusViewModel.account.avatar,
|
||||
rebloggedAccountDisplayName: self.statusViewModel.reblogStatus?.account.displayNameWithoutEmojis,
|
||||
rebloggedAccountAvatar: self.statusViewModel.reblogStatus?.account.avatar) {
|
||||
self.routerPath.navigate(to: .userProfile(accountId: self.statusViewModel.account.id,
|
||||
accountDisplayName: self.statusViewModel.account.displayNameWithoutEmojis,
|
||||
accountUserName: self.statusViewModel.account.acct))
|
||||
|
|
|
@ -27,7 +27,7 @@ public class StatusFetcher {
|
|||
}
|
||||
|
||||
let client = PixelfedClient(baseURL: account.serverUrl).getAuthenticated(token: accessToken)
|
||||
let statuses = try await client.getHomeTimeline(limit: 20)
|
||||
let statuses = try await client.getHomeTimeline(limit: 20, includeReblogs: defaultSettings.showReboostedStatuses)
|
||||
var widgetEntries: [PhotoWidgetEntry] = []
|
||||
|
||||
for status in statuses {
|
||||
|
|
|
@ -29,7 +29,8 @@ public struct ContentWarning<Content: View, Blurred: View>: View {
|
|||
.transition(.opacity)
|
||||
|
||||
VStack(alignment: .trailing) {
|
||||
HStack(alignment: .top) {
|
||||
Spacer()
|
||||
HStack(alignment: .bottom) {
|
||||
Spacer()
|
||||
Button {
|
||||
withAnimation {
|
||||
|
@ -40,12 +41,10 @@ public struct ContentWarning<Content: View, Blurred: View>: View {
|
|||
.font(.system(size: 18))
|
||||
.foregroundColor(.white.opacity(0.8))
|
||||
.shadow(color: Color.black, radius: 1)
|
||||
.padding(.top, 11)
|
||||
.padding(.trailing, 16)
|
||||
.padding([.bottom, .leading], 16)
|
||||
.padding([.top, .bottom, .leading], 12)
|
||||
}
|
||||
}
|
||||
Spacer()
|
||||
.padding(.trailing, 64)
|
||||
}
|
||||
.foregroundColor(.mainTextColor)
|
||||
}
|
||||
|
|
|
@ -14,43 +14,62 @@ public struct ImageAvatar: View {
|
|||
|
||||
private let displayName: String?
|
||||
private let avatarUrl: URL?
|
||||
private let rebloggedAccountDisplayName: String?
|
||||
private let rebloggedAccountAvatar: URL?
|
||||
private let onTap: () -> Void
|
||||
|
||||
public init(displayName: String?, avatarUrl: URL?, onTap: @escaping () -> Void) {
|
||||
|
||||
public init(displayName: String?, avatarUrl: URL?, rebloggedAccountDisplayName: String?, rebloggedAccountAvatar: URL?, onTap: @escaping () -> Void) {
|
||||
self.displayName = displayName
|
||||
self.avatarUrl = avatarUrl
|
||||
self.rebloggedAccountAvatar = rebloggedAccountAvatar
|
||||
self.rebloggedAccountDisplayName = rebloggedAccountDisplayName
|
||||
self.onTap = onTap
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
if self.applicationState.showAvatarsOnTimeline {
|
||||
VStack(alignment: .leading) {
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
HStack(alignment: .center) {
|
||||
HStack(alignment: .center) {
|
||||
LazyImage(url: avatarUrl) { state in
|
||||
if let image = state.image {
|
||||
self.buildAvatar(image: image)
|
||||
} else if state.isLoading {
|
||||
self.buildAvatar()
|
||||
} else {
|
||||
self.buildAvatar()
|
||||
}
|
||||
LazyImage(url: avatarUrl) { state in
|
||||
if let image = state.image {
|
||||
self.buildAvatar(image: image)
|
||||
} else if state.isLoading {
|
||||
self.buildAvatar()
|
||||
} else {
|
||||
self.buildAvatar()
|
||||
}
|
||||
|
||||
Text(displayName ?? "")
|
||||
.font(.system(size: 15))
|
||||
.foregroundColor(.white.opacity(0.8))
|
||||
.fontWeight(.semibold)
|
||||
.shadow(color: .black, radius: 2)
|
||||
}
|
||||
.padding(8)
|
||||
.onTapGesture {
|
||||
self.onTap()
|
||||
}
|
||||
|
||||
Text(displayName ?? "")
|
||||
.lineLimit(1)
|
||||
.font(.system(size: 15))
|
||||
.foregroundColor(.white.opacity(0.8))
|
||||
.fontWeight(.semibold)
|
||||
.shadow(color: .black, radius: 2)
|
||||
|
||||
Spacer()
|
||||
|
||||
if let rebloggedAccountAvatar = self.rebloggedAccountAvatar,
|
||||
let rebloggedAccountDisplayName = self.rebloggedAccountDisplayName {
|
||||
HStack(alignment: .center, spacing: 4) {
|
||||
UserAvatar(accountAvatar: rebloggedAccountAvatar, size: .mini)
|
||||
Text(rebloggedAccountDisplayName)
|
||||
.lineLimit(1)
|
||||
Image("custom.rocket")
|
||||
.padding(.trailing, 8)
|
||||
}
|
||||
.font(.footnote)
|
||||
.foregroundColor(.white.opacity(0.8))
|
||||
.background(.black.opacity(0.4))
|
||||
.clipShape(Capsule())
|
||||
.padding(.leading, 32)
|
||||
}
|
||||
}
|
||||
|
||||
.padding(8)
|
||||
.onTapGesture {
|
||||
self.onTap()
|
||||
}
|
||||
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue