Ordered timelines

This commit is contained in:
Justin Mazzocchi 2021-01-31 07:42:44 -08:00
parent 35a90f866c
commit 2dae234849
No known key found for this signature in database
GPG key ID: E223E6937AAFB01C
8 changed files with 49 additions and 7 deletions

View file

@ -128,6 +128,7 @@ extension ContentDatabase {
.references("timelineRecord", onDelete: .cascade) .references("timelineRecord", onDelete: .cascade)
t.column("statusId", .text).indexed().notNull() t.column("statusId", .text).indexed().notNull()
.references("statusRecord", onDelete: .cascade) .references("statusRecord", onDelete: .cascade)
t.column("order", .integer)
t.primaryKey(["timelineId", "statusId"], onConflict: .replace) t.primaryKey(["timelineId", "statusId"], onConflict: .replace)
} }

View file

@ -58,6 +58,7 @@ public extension ContentDatabase {
.eraseToAnyPublisher() .eraseToAnyPublisher()
} }
// swiftlint:disable:next function_body_length
func insert( func insert(
statuses: [Status], statuses: [Status],
timeline: Timeline, timeline: Timeline,
@ -69,10 +70,24 @@ public extension ContentDatabase {
let maxIdPresent = try String.fetchOne($0, timelineRecord.statuses.select(max(StatusRecord.Columns.id))) let maxIdPresent = try String.fetchOne($0, timelineRecord.statuses.select(max(StatusRecord.Columns.id)))
var order = timeline.ordered
? try Int.fetchOne(
$0,
TimelineStatusJoin.filter(TimelineStatusJoin.Columns.timelineId == timeline.id)
.select(max(TimelineStatusJoin.Columns.order)))
: nil
for status in statuses { for status in statuses {
try status.save($0) try status.save($0)
try TimelineStatusJoin(timelineId: timeline.id, statusId: status.id).save($0) if let order = order {
print("saving with order: \(order)")
}
try TimelineStatusJoin(timelineId: timeline.id, statusId: status.id, order: order).save($0)
if let presentOrder = order {
order = presentOrder + 1
}
} }
if let maxIdPresent = maxIdPresent, if let maxIdPresent = maxIdPresent,
@ -447,7 +462,8 @@ public extension ContentDatabase {
func timelinePublisher(_ timeline: Timeline) -> AnyPublisher<[CollectionSection], Error> { func timelinePublisher(_ timeline: Timeline) -> AnyPublisher<[CollectionSection], Error> {
ValueObservation.tracking( ValueObservation.tracking(
TimelineItemsInfo.request(TimelineRecord.filter(TimelineRecord.Columns.id == timeline.id)).fetchOne) TimelineItemsInfo.request(TimelineRecord.filter(TimelineRecord.Columns.id == timeline.id),
ordered: timeline.ordered).fetchOne)
.removeDuplicates() .removeDuplicates()
.publisher(in: databaseWriter) .publisher(in: databaseWriter)
.handleEvents( .handleEvents(

View file

@ -17,15 +17,17 @@ extension TimelineItemsInfo {
let pinnedStatusInfos: [StatusInfo] let pinnedStatusInfos: [StatusInfo]
} }
static func addingIncludes<T: DerivableRequest>( _ request: T) -> T where T.RowDecoder == TimelineRecord { static func addingIncludes<T: DerivableRequest>( _ request: T, ordered: Bool) -> T where T.RowDecoder == TimelineRecord {
request.including(all: StatusInfo.addingIncludes(TimelineRecord.statuses).forKey(CodingKeys.statusInfos)) let statusesAssociation = ordered ? TimelineRecord.orderedStatuses : TimelineRecord.statuses
return request.including(all: StatusInfo.addingIncludes(statusesAssociation).forKey(CodingKeys.statusInfos))
.including(all: TimelineRecord.loadMores.forKey(CodingKeys.loadMoreRecords)) .including(all: TimelineRecord.loadMores.forKey(CodingKeys.loadMoreRecords))
.including(optional: PinnedStatusesInfo.addingIncludes(TimelineRecord.account) .including(optional: PinnedStatusesInfo.addingIncludes(TimelineRecord.account)
.forKey(CodingKeys.pinnedStatusesInfo)) .forKey(CodingKeys.pinnedStatusesInfo))
} }
static func request(_ request: QueryInterfaceRequest<TimelineRecord>) -> QueryInterfaceRequest<Self> { static func request(_ request: QueryInterfaceRequest<TimelineRecord>, ordered: Bool) -> QueryInterfaceRequest<Self> {
addingIncludes(request).asRequest(of: self) addingIncludes(request, ordered: ordered).asRequest(of: self)
} }
func items(filters: [Filter]) -> [CollectionSection] { func items(filters: [Filter]) -> [CollectionSection] {

View file

@ -29,6 +29,10 @@ extension TimelineRecord {
through: statusJoins, through: statusJoins,
using: TimelineStatusJoin.status) using: TimelineStatusJoin.status)
.order(StatusRecord.Columns.id.desc) .order(StatusRecord.Columns.id.desc)
static let orderedStatuses = hasMany(
StatusRecord.self,
through: statusJoins.order(TimelineStatusJoin.Columns.order),
using: TimelineStatusJoin.status)
static let account = belongsTo(AccountRecord.self, using: ForeignKey([Columns.accountId])) static let account = belongsTo(AccountRecord.self, using: ForeignKey([Columns.accountId]))
static let loadMores = hasMany(LoadMoreRecord.self) static let loadMores = hasMany(LoadMoreRecord.self)
@ -36,6 +40,10 @@ extension TimelineRecord {
StatusInfo.request(request(for: Self.statuses)) StatusInfo.request(request(for: Self.statuses))
} }
var orderedStatuses: QueryInterfaceRequest<StatusInfo> {
StatusInfo.request(request(for: Self.orderedStatuses))
}
var loadMores: QueryInterfaceRequest<LoadMoreRecord> { var loadMores: QueryInterfaceRequest<LoadMoreRecord> {
request(for: Self.loadMores) request(for: Self.loadMores)
} }

View file

@ -7,6 +7,7 @@ import Mastodon
struct TimelineStatusJoin: ContentDatabaseRecord { struct TimelineStatusJoin: ContentDatabaseRecord {
let timelineId: Timeline.Id let timelineId: Timeline.Id
let statusId: Status.Id let statusId: Status.Id
let order: Int?
static let status = belongsTo(StatusRecord.self) static let status = belongsTo(StatusRecord.self)
} }
@ -15,5 +16,6 @@ extension TimelineStatusJoin {
enum Columns { enum Columns {
static let timelineId = Column(CodingKeys.timelineId) static let timelineId = Column(CodingKeys.timelineId)
static let statusId = Column(CodingKeys.statusId) static let statusId = Column(CodingKeys.statusId)
static let order = Column(CodingKeys.order)
} }
} }

View file

@ -32,6 +32,15 @@ public extension Timeline {
return nil return nil
} }
} }
var ordered: Bool {
switch self {
case .favorites, .bookmarks:
return true
default:
return false
}
}
} }
extension Timeline: Identifiable { extension Timeline: Identifiable {

View file

@ -10,7 +10,6 @@ public struct TimelineService {
public let sections: AnyPublisher<[CollectionSection], Error> public let sections: AnyPublisher<[CollectionSection], Error>
public let navigationService: NavigationService public let navigationService: NavigationService
public let nextPageMaxId: AnyPublisher<String, Never> public let nextPageMaxId: AnyPublisher<String, Never>
public let preferLastPresentIdOverNextPageMaxId = true
public let title: AnyPublisher<String, Never> public let title: AnyPublisher<String, Never>
public let titleLocalizationComponents: AnyPublisher<[String], Never> public let titleLocalizationComponents: AnyPublisher<[String], Never>
@ -48,6 +47,10 @@ public struct TimelineService {
} }
extension TimelineService: CollectionService { extension TimelineService: CollectionService {
public var preferLastPresentIdOverNextPageMaxId: Bool {
!timeline.ordered
}
public var markerTimeline: Marker.Timeline? { public var markerTimeline: Marker.Timeline? {
switch timeline { switch timeline {
case .home: case .home:

View file

@ -33,6 +33,7 @@ final class AttachmentsView: UIView {
let attachmentView = AttachmentView(viewModel: attachmentViewModel, parentViewModel: viewModel) let attachmentView = AttachmentView(viewModel: attachmentViewModel, parentViewModel: viewModel)
attachmentView.playing = viewModel.shouldShowAttachments && attachmentViewModel.shouldAutoplay attachmentView.playing = viewModel.shouldShowAttachments && attachmentViewModel.shouldAutoplay
attachmentView.removeButton.isHidden = !viewModel.canRemoveAttachments attachmentView.removeButton.isHidden = !viewModel.canRemoveAttachments
attachmentView.editIcon.isHidden = !viewModel.canRemoveAttachments
if viewModel.attachmentViewModels.count == 2 && index == 1 if viewModel.attachmentViewModels.count == 2 && index == 1
|| viewModel.attachmentViewModels.count == 3 && index != 0 || viewModel.attachmentViewModels.count == 3 && index != 0