Fix and simplify startup position

This commit is contained in:
Justin Mazzocchi 2021-02-08 11:07:59 -08:00
parent 728fdddca1
commit 3149fd8edf
No known key found for this signature in database
GPG key ID: E223E6937AAFB01C
7 changed files with 17 additions and 109 deletions

View file

@ -16,7 +16,6 @@ public struct ContentDatabase {
public init(id: Identity.Id, public init(id: Identity.Id,
useHomeTimelineLastReadId: Bool, useHomeTimelineLastReadId: Bool,
useNotificationsLastReadId: Bool,
inMemory: Bool, inMemory: Bool,
appGroup: String, appGroup: String,
keychain: Keychain.Type) throws { keychain: Keychain.Type) throws {
@ -35,8 +34,7 @@ public struct ContentDatabase {
try Self.clean( try Self.clean(
databaseWriter, databaseWriter,
useHomeTimelineLastReadId: useHomeTimelineLastReadId, useHomeTimelineLastReadId: useHomeTimelineLastReadId)
useNotificationsLastReadId: useNotificationsLastReadId)
activeFiltersPublisher = ValueObservation.tracking { activeFiltersPublisher = ValueObservation.tracking {
try Filter.filter(Filter.Columns.expiresAt == nil || Filter.Columns.expiresAt > Date()).fetchAll($0) try Filter.filter(Filter.Columns.expiresAt == nil || Filter.Columns.expiresAt > Date()).fetchAll($0)
@ -708,48 +706,14 @@ private extension ContentDatabase {
// swiftlint:disable:next function_body_length // swiftlint:disable:next function_body_length
static func clean(_ databaseWriter: DatabaseWriter, static func clean(_ databaseWriter: DatabaseWriter,
useHomeTimelineLastReadId: Bool, useHomeTimelineLastReadId: Bool) throws {
useNotificationsLastReadId: Bool) throws {
try databaseWriter.write { try databaseWriter.write {
let notificationAccountIds: [Account.Id] try NotificationRecord.deleteAll($0)
let notificationStatusIds: [Status.Id]
try ConversationRecord.deleteAll($0) try ConversationRecord.deleteAll($0)
try StatusAncestorJoin.deleteAll($0) try StatusAncestorJoin.deleteAll($0)
try StatusDescendantJoin.deleteAll($0) try StatusDescendantJoin.deleteAll($0)
try AccountList.deleteAll($0) try AccountList.deleteAll($0)
if useNotificationsLastReadId {
var notificationIds = try MastodonNotification.Id.fetchAll(
$0,
NotificationRecord.select(NotificationRecord.Columns.id)
.order(NotificationRecord.Columns.id.desc))
if let lastReadId = try MastodonNotification.Id.fetchOne(
$0,
LastReadIdRecord.filter(
LastReadIdRecord.Columns.markerTimeline == Marker.Timeline.notifications.rawValue)
.select(LastReadIdRecord.Columns.id))
?? notificationIds.first,
let index = notificationIds.firstIndex(of: lastReadId) {
notificationIds = Array(notificationIds.prefix(index + Self.cleanAfterLastReadIdCount))
}
try NotificationRecord.filter(!notificationIds.contains(NotificationRecord.Columns.id)).deleteAll($0)
notificationAccountIds = try Account.Id.fetchAll(
$0,
NotificationRecord.select(NotificationRecord.Columns.accountId))
notificationStatusIds = try Status.Id.fetchAll(
$0,
NotificationRecord.filter(
NotificationRecord.Columns.statusId != nil)
.select(NotificationRecord.Columns.statusId))
} else {
try NotificationRecord.deleteAll($0)
notificationAccountIds = []
notificationStatusIds = []
}
if useHomeTimelineLastReadId { if useHomeTimelineLastReadId {
try TimelineRecord.filter(TimelineRecord.Columns.id != Timeline.home.id).deleteAll($0) try TimelineRecord.filter(TimelineRecord.Columns.id != Timeline.home.id).deleteAll($0)
var statusIds = try Status.Id.fetchAll( var statusIds = try Status.Id.fetchAll(
@ -766,7 +730,6 @@ private extension ContentDatabase {
statusIds = Array(statusIds.prefix(index + Self.cleanAfterLastReadIdCount)) statusIds = Array(statusIds.prefix(index + Self.cleanAfterLastReadIdCount))
} }
statusIds += notificationStatusIds
statusIds += try Status.Id.fetchAll( statusIds += try Status.Id.fetchAll(
$0, $0,
StatusRecord.filter(statusIds.contains(StatusRecord.Columns.id) StatusRecord.filter(statusIds.contains(StatusRecord.Columns.id)
@ -774,7 +737,6 @@ private extension ContentDatabase {
.select(StatusRecord.Columns.reblogId)) .select(StatusRecord.Columns.reblogId))
try StatusRecord.filter(!statusIds.contains(StatusRecord.Columns.id)).deleteAll($0) try StatusRecord.filter(!statusIds.contains(StatusRecord.Columns.id)).deleteAll($0)
var accountIds = try Account.Id.fetchAll($0, StatusRecord.select(StatusRecord.Columns.accountId)) var accountIds = try Account.Id.fetchAll($0, StatusRecord.select(StatusRecord.Columns.accountId))
accountIds += notificationAccountIds
accountIds += try Account.Id.fetchAll( accountIds += try Account.Id.fetchAll(
$0, $0,
AccountRecord.filter(accountIds.contains(AccountRecord.Columns.id) AccountRecord.filter(accountIds.contains(AccountRecord.Columns.id)
@ -783,8 +745,8 @@ private extension ContentDatabase {
try AccountRecord.filter(!accountIds.contains(AccountRecord.Columns.id)).deleteAll($0) try AccountRecord.filter(!accountIds.contains(AccountRecord.Columns.id)).deleteAll($0)
} else { } else {
try TimelineRecord.deleteAll($0) try TimelineRecord.deleteAll($0)
try StatusRecord.filter(!notificationStatusIds.contains(StatusRecord.Columns.id)).deleteAll($0) try StatusRecord.deleteAll($0)
try AccountRecord.filter(!notificationAccountIds.contains(AccountRecord.Columns.id)).deleteAll($0) try AccountRecord.deleteAll($0)
} }
} }
} }

View file

@ -198,7 +198,6 @@
"preferences.home-timeline-position-on-startup" = "Home timeline position on startup"; "preferences.home-timeline-position-on-startup" = "Home timeline position on startup";
"preferences.notifications-position-on-startup" = "Notifications position on startup"; "preferences.notifications-position-on-startup" = "Notifications position on startup";
"preferences.position.remember-position" = "Remember position"; "preferences.position.remember-position" = "Remember position";
"preferences.position.sync-position" = "Sync position with web and other devices";
"preferences.position.newest" = "Load newest"; "preferences.position.newest" = "Load newest";
"preferences.require-double-tap-to-reblog" = "Require double tap to reblog"; "preferences.require-double-tap-to-reblog" = "Require double tap to reblog";
"preferences.require-double-tap-to-favorite" = "Require double tap to favorite"; "preferences.require-double-tap-to-favorite" = "Require double tap to favorite";

View file

@ -34,7 +34,6 @@ public struct IdentityService {
contentDatabase = try ContentDatabase( contentDatabase = try ContentDatabase(
id: id, id: id,
useHomeTimelineLastReadId: appPreferences.homeTimelineBehavior == .rememberPosition, useHomeTimelineLastReadId: appPreferences.homeTimelineBehavior == .rememberPosition,
useNotificationsLastReadId: appPreferences.notificationsTabBehavior == .rememberPosition,
inMemory: environment.inMemoryContent, inMemory: environment.inMemoryContent,
appGroup: AppEnvironment.appGroup, appGroup: AppEnvironment.appGroup,
keychain: environment.keychain) keychain: environment.keychain)
@ -134,10 +133,6 @@ public extension IdentityService {
switch AppPreferences(environment: environment).positionBehavior(markerTimeline: markerTimeline) { switch AppPreferences(environment: environment).positionBehavior(markerTimeline: markerTimeline) {
case .rememberPosition: case .rememberPosition:
return contentDatabase.setLastReadId(id, markerTimeline: markerTimeline) return contentDatabase.setLastReadId(id, markerTimeline: markerTimeline)
case .syncPosition:
return mastodonAPIClient.request(MarkersEndpoint.post([markerTimeline: id]))
.ignoreOutput()
.eraseToAnyPublisher()
case .newest: case .newest:
return Empty().eraseToAnyPublisher() return Empty().eraseToAnyPublisher()
} }

View file

@ -40,7 +40,6 @@ public extension AppPreferences {
enum PositionBehavior: String, CaseIterable, Identifiable { enum PositionBehavior: String, CaseIterable, Identifiable {
case rememberPosition case rememberPosition
case syncPosition
case newest case newest
public var id: String { rawValue } public var id: String { rawValue }
@ -116,18 +115,6 @@ public extension AppPreferences {
set { self[.homeTimelineBehavior] = newValue.rawValue } set { self[.homeTimelineBehavior] = newValue.rawValue }
} }
var notificationsTabBehavior: PositionBehavior {
get {
if let rawValue = self[.notificationsTabBehavior] as String?,
let value = PositionBehavior(rawValue: rawValue) {
return value
}
return .newest
}
set { self[.notificationsTabBehavior] = newValue.rawValue }
}
var defaultEmojiSkinTone: SystemEmoji.SkinTone? { var defaultEmojiSkinTone: SystemEmoji.SkinTone? {
get { get {
if let rawValue = self[.defaultEmojiSkinTone] as Int?, if let rawValue = self[.defaultEmojiSkinTone] as Int?,
@ -158,7 +145,7 @@ public extension AppPreferences {
case .home: case .home:
return homeTimelineBehavior return homeTimelineBehavior
case .notifications: case .notifications:
return notificationsTabBehavior return .newest
} }
} }

View file

@ -51,7 +51,6 @@ extension ContentDatabase {
static let preview = try! ContentDatabase( static let preview = try! ContentDatabase(
id: identityId, id: identityId,
useHomeTimelineLastReadId: false, useHomeTimelineLastReadId: false,
useNotificationsLastReadId: false,
inMemory: true, inMemory: true,
appGroup: "group.metabolist.metatext", appGroup: "group.metabolist.metatext",
keychain: MockKeychain.self) keychain: MockKeychain.self)

View file

@ -21,7 +21,7 @@ public class CollectionItemsViewModel: ObservableObject {
private let lastReadId = CurrentValueSubject<String?, Never>(nil) private let lastReadId = CurrentValueSubject<String?, Never>(nil)
private var lastSelectedLoadMore: LoadMore? private var lastSelectedLoadMore: LoadMore?
private var hasRequestedUsingMarker = false private var hasRequestedUsingMarker = false
private var shouldRestorePositionOfLocalLastReadId = false private var markerScrollPositionItemId: CollectionItem.Id?
private var cancellables = Set<AnyCancellable>() private var cancellables = Set<AnyCancellable>()
public init(collectionService: CollectionService, identityContext: IdentityContext) { public init(collectionService: CollectionService, identityContext: IdentityContext) {
@ -50,8 +50,10 @@ public class CollectionItemsViewModel: ObservableObject {
.store(in: &cancellables) .store(in: &cancellables)
if let markerTimeline = collectionService.markerTimeline { if let markerTimeline = collectionService.markerTimeline {
shouldRestorePositionOfLocalLastReadId = if identityContext.appPreferences.positionBehavior(markerTimeline: markerTimeline) == .rememberPosition {
identityContext.appPreferences.positionBehavior(markerTimeline: markerTimeline) == .rememberPosition markerScrollPositionItemId = identityContext.service.getLocalLastReadId(markerTimeline)
}
lastReadId.compactMap { $0 } lastReadId.compactMap { $0 }
.removeDuplicates() .removeDuplicates()
.debounce(for: .seconds(Self.lastReadIdDebounceInterval), scheduler: DispatchQueue.global()) .debounce(for: .seconds(Self.lastReadIdDebounceInterval), scheduler: DispatchQueue.global())
@ -102,32 +104,7 @@ extension CollectionItemsViewModel: CollectionViewModel {
public var canRefresh: Bool { collectionService.canRefresh } public var canRefresh: Bool { collectionService.canRefresh }
public func request(maxId: String? = nil, minId: String? = nil, search: Search?) { public func request(maxId: String? = nil, minId: String? = nil, search: Search?) {
let publisher: AnyPublisher<Never, Error> collectionService.request(maxId: realMaxId(maxId: maxId), minId: minId, search: search)
if let markerTimeline = collectionService.markerTimeline,
identityContext.appPreferences.positionBehavior(markerTimeline: markerTimeline) == .syncPosition,
!hasRequestedUsingMarker {
publisher = identityContext.service.getMarker(markerTimeline)
.flatMap { [weak self] in
self?.collectionService.request(maxId: $0.lastReadId, minId: nil, search: nil)
?? Empty().eraseToAnyPublisher()
}
.catch { [weak self] _ in
self?.collectionService.request(maxId: nil, minId: nil, search: nil)
?? Empty().eraseToAnyPublisher()
}
.collect()
.flatMap { [weak self] _ in
self?.collectionService.request(maxId: nil, minId: nil, search: nil)
?? Empty().eraseToAnyPublisher()
}
.eraseToAnyPublisher()
self.hasRequestedUsingMarker = true
} else {
publisher = collectionService.request(maxId: realMaxId(maxId: maxId), minId: minId, search: search)
}
publisher
.receive(on: DispatchQueue.main) .receive(on: DispatchQueue.main)
.assignErrorsToAlertItem(to: \.alertItem, on: self) .assignErrorsToAlertItem(to: \.alertItem, on: self)
.handleEvents( .handleEvents(
@ -180,8 +157,7 @@ extension CollectionItemsViewModel: CollectionViewModel {
public func viewedAtTop(indexPath: IndexPath) { public func viewedAtTop(indexPath: IndexPath) {
topVisibleIndexPath = indexPath topVisibleIndexPath = indexPath
if !shouldRestorePositionOfLocalLastReadId, if lastUpdate.sections.count > indexPath.section,
lastUpdate.sections.count > indexPath.section,
lastUpdate.sections[indexPath.section].items.count > indexPath.item { lastUpdate.sections[indexPath.section].items.count > indexPath.item {
lastReadId.send(lastUpdate.sections[indexPath.section].items[indexPath.item].itemId) lastReadId.send(lastUpdate.sections[indexPath.section].items[indexPath.item].itemId)
} }
@ -387,13 +363,11 @@ private extension CollectionItemsViewModel {
let items = lastUpdate.sections.map(\.items).reduce([], +) let items = lastUpdate.sections.map(\.items).reduce([], +)
let newItems = newSections.map(\.items).reduce([], +) let newItems = newSections.map(\.items).reduce([], +)
if shouldRestorePositionOfLocalLastReadId, if let itemId = markerScrollPositionItemId,
let markerTimeline = collectionService.markerTimeline, newItems.contains(where: { $0.itemId == itemId }) {
let localLastReadId = identityContext.service.getLocalLastReadId(markerTimeline), markerScrollPositionItemId = nil
newItems.contains(where: { $0.itemId == localLastReadId }) {
shouldRestorePositionOfLocalLastReadId = false
return localLastReadId return itemId
} }
if collectionService is ContextService, if collectionService is ContextService,

View file

@ -121,12 +121,6 @@ struct PreferencesView: View {
Text(option.localizedStringKey).tag(option) Text(option.localizedStringKey).tag(option)
} }
} }
Picker("preferences.notifications-position-on-startup",
selection: $identityContext.appPreferences.notificationsTabBehavior) {
ForEach(AppPreferences.PositionBehavior.allCases) { option in
Text(option.localizedStringKey).tag(option)
}
}
} }
} }
} }
@ -183,8 +177,6 @@ extension AppPreferences.PositionBehavior {
switch self { switch self {
case .rememberPosition: case .rememberPosition:
return "preferences.position.remember-position" return "preferences.position.remember-position"
case .syncPosition:
return "preferences.position.sync-position"
case .newest: case .newest:
return "preferences.position.newest" return "preferences.position.newest"
} }