Refactor the NSDiffableDataSourceLayer (#1235)

* first part, using references

* better animations

* delete view provider and replaced wth two files

* ordered dictionary usage

* bubbled styler view previews

* plain style is back

* fix

* read marker previews restored

* updated tests

* code improvements

* better naming
This commit is contained in:
Mauro
2023-07-04 16:08:43 +02:00
committed by GitHub
parent eba3e5d5e6
commit b294278170
15 changed files with 376 additions and 316 deletions
@@ -0,0 +1,70 @@
//
// Copyright 2022 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import SwiftUI
struct RoomTimelineItemView: View {
@ObservedObject var viewModel: RoomTimelineItemViewModel
var body: some View {
timelineView
.environment(\.timelineGroupStyle, viewModel.groupStyle)
}
@ViewBuilder private var timelineView: some View {
switch viewModel.type {
case .text(let item):
TextRoomTimelineView(timelineItem: item)
case .separator(let item):
SeparatorRoomTimelineView(timelineItem: item)
case .image(let item):
ImageRoomTimelineView(timelineItem: item)
case .video(let item):
VideoRoomTimelineView(timelineItem: item)
case .audio(let item):
AudioRoomTimelineView(timelineItem: item)
case .file(let item):
FileRoomTimelineView(timelineItem: item)
case .emote(let item):
EmoteRoomTimelineView(timelineItem: item)
case .notice(let item):
NoticeRoomTimelineView(timelineItem: item)
case .redacted(let item):
RedactedRoomTimelineView(timelineItem: item)
case .encrypted(let item):
EncryptedRoomTimelineView(timelineItem: item)
case .readMarker(let item):
ReadMarkerRoomTimelineView(timelineItem: item)
case .paginationIndicator(let item):
PaginationIndicatorRoomTimelineView(timelineItem: item)
case .sticker(let item):
StickerRoomTimelineView(timelineItem: item)
case .unsupported(let item):
UnsupportedRoomTimelineView(timelineItem: item)
case .timelineStart(let item):
TimelineStartRoomTimelineView(timelineItem: item)
case .state(let item):
StateRoomTimelineView(timelineItem: item)
case .group(let item):
CollapsibleRoomTimelineView(timelineItem: item)
case .location(let item):
LocationRoomTimelineView(timelineItem: item)
}
}
var timelineGroupStyle: TimelineGroupStyle {
viewModel.groupStyle
}
}
@@ -0,0 +1,146 @@
//
// Copyright 2023 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import Foundation
final class RoomTimelineItemViewModel: Identifiable, Equatable, ObservableObject {
static func == (lhs: RoomTimelineItemViewModel, rhs: RoomTimelineItemViewModel) -> Bool {
lhs.type == rhs.type && lhs.groupStyle == rhs.groupStyle
}
@Published var type: RoomTimelineItemType
@Published var groupStyle: TimelineGroupStyle
var id: String {
type.id
}
convenience init(item: RoomTimelineItemProtocol, groupStyle: TimelineGroupStyle) {
self.init(type: .init(item: item), groupStyle: groupStyle)
}
init(type: RoomTimelineItemType, groupStyle: TimelineGroupStyle) {
self.type = type
self.groupStyle = groupStyle
}
var isReactable: Bool {
type.isReactable
}
}
enum RoomTimelineItemType: Equatable {
case text(TextRoomTimelineItem)
case separator(SeparatorRoomTimelineItem)
case image(ImageRoomTimelineItem)
case video(VideoRoomTimelineItem)
case audio(AudioRoomTimelineItem)
case file(FileRoomTimelineItem)
case emote(EmoteRoomTimelineItem)
case notice(NoticeRoomTimelineItem)
case redacted(RedactedRoomTimelineItem)
case encrypted(EncryptedRoomTimelineItem)
case readMarker(ReadMarkerRoomTimelineItem)
case paginationIndicator(PaginationIndicatorRoomTimelineItem)
case sticker(StickerRoomTimelineItem)
case unsupported(UnsupportedRoomTimelineItem)
case timelineStart(TimelineStartRoomTimelineItem)
case state(StateRoomTimelineItem)
case group(CollapsibleTimelineItem)
case location(LocationRoomTimelineItem)
// swiftlint:disable:next cyclomatic_complexity
init(item: RoomTimelineItemProtocol) {
switch item {
case let item as TextRoomTimelineItem:
self = .text(item)
case let item as ImageRoomTimelineItem:
self = .image(item)
case let item as VideoRoomTimelineItem:
self = .video(item)
case let item as AudioRoomTimelineItem:
self = .audio(item)
case let item as FileRoomTimelineItem:
self = .file(item)
case let item as SeparatorRoomTimelineItem:
self = .separator(item)
case let item as NoticeRoomTimelineItem:
self = .notice(item)
case let item as EmoteRoomTimelineItem:
self = .emote(item)
case let item as RedactedRoomTimelineItem:
self = .redacted(item)
case let item as EncryptedRoomTimelineItem:
self = .encrypted(item)
case let item as ReadMarkerRoomTimelineItem:
self = .readMarker(item)
case let item as PaginationIndicatorRoomTimelineItem:
self = .paginationIndicator(item)
case let item as StickerRoomTimelineItem:
self = .sticker(item)
case let item as UnsupportedRoomTimelineItem:
self = .unsupported(item)
case let item as TimelineStartRoomTimelineItem:
self = .timelineStart(item)
case let item as StateRoomTimelineItem:
self = .state(item)
case let item as CollapsibleTimelineItem:
self = .group(item)
case let item as LocationRoomTimelineItem:
self = .location(item)
default:
fatalError("Unknown timeline item")
}
}
var id: String {
switch self {
case .text(let item as RoomTimelineItemProtocol),
.separator(let item as RoomTimelineItemProtocol),
.image(let item as RoomTimelineItemProtocol),
.video(let item as RoomTimelineItemProtocol),
.audio(let item as RoomTimelineItemProtocol),
.file(let item as RoomTimelineItemProtocol),
.emote(let item as RoomTimelineItemProtocol),
.notice(let item as RoomTimelineItemProtocol),
.redacted(let item as RoomTimelineItemProtocol),
.encrypted(let item as RoomTimelineItemProtocol),
.readMarker(let item as RoomTimelineItemProtocol),
.paginationIndicator(let item as RoomTimelineItemProtocol),
.sticker(let item as RoomTimelineItemProtocol),
.unsupported(let item as RoomTimelineItemProtocol),
.timelineStart(let item as RoomTimelineItemProtocol),
.state(let item as RoomTimelineItemProtocol),
.group(let item as RoomTimelineItemProtocol),
.location(let item as RoomTimelineItemProtocol):
return item.id
}
}
/// Whether or not it is possible to send a reaction to this timeline item.
var isReactable: Bool {
switch self {
case .text, .image, .video, .audio, .file, .emote, .notice, .sticker, .location:
return true
case .redacted, .encrypted, .unsupported, .state: // Event based items that aren't reactable
return false
case .timelineStart, .separator, .readMarker, .paginationIndicator: // Virtual items are never reactable
return false
case .group:
return false
}
}
}
@@ -1,193 +0,0 @@
//
// Copyright 2022 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import Foundation
import SwiftUI
enum RoomTimelineViewProvider: Identifiable, Hashable {
case text(TextRoomTimelineItem, TimelineGroupStyle)
case separator(SeparatorRoomTimelineItem, TimelineGroupStyle)
case image(ImageRoomTimelineItem, TimelineGroupStyle)
case video(VideoRoomTimelineItem, TimelineGroupStyle)
case audio(AudioRoomTimelineItem, TimelineGroupStyle)
case file(FileRoomTimelineItem, TimelineGroupStyle)
case emote(EmoteRoomTimelineItem, TimelineGroupStyle)
case notice(NoticeRoomTimelineItem, TimelineGroupStyle)
case redacted(RedactedRoomTimelineItem, TimelineGroupStyle)
case encrypted(EncryptedRoomTimelineItem, TimelineGroupStyle)
case readMarker(ReadMarkerRoomTimelineItem, TimelineGroupStyle)
case paginationIndicator(PaginationIndicatorRoomTimelineItem, TimelineGroupStyle)
case sticker(StickerRoomTimelineItem, TimelineGroupStyle)
case unsupported(UnsupportedRoomTimelineItem, TimelineGroupStyle)
case timelineStart(TimelineStartRoomTimelineItem, TimelineGroupStyle)
case state(StateRoomTimelineItem, TimelineGroupStyle)
case group(CollapsibleTimelineItem, TimelineGroupStyle)
case location(LocationRoomTimelineItem, TimelineGroupStyle)
// swiftlint:disable:next cyclomatic_complexity
init(timelineItem: RoomTimelineItemProtocol, groupStyle: TimelineGroupStyle) {
switch timelineItem {
case let item as TextRoomTimelineItem:
self = .text(item, groupStyle)
case let item as ImageRoomTimelineItem:
self = .image(item, groupStyle)
case let item as VideoRoomTimelineItem:
self = .video(item, groupStyle)
case let item as AudioRoomTimelineItem:
self = .audio(item, groupStyle)
case let item as FileRoomTimelineItem:
self = .file(item, groupStyle)
case let item as SeparatorRoomTimelineItem:
self = .separator(item, groupStyle)
case let item as NoticeRoomTimelineItem:
self = .notice(item, groupStyle)
case let item as EmoteRoomTimelineItem:
self = .emote(item, groupStyle)
case let item as RedactedRoomTimelineItem:
self = .redacted(item, groupStyle)
case let item as EncryptedRoomTimelineItem:
self = .encrypted(item, groupStyle)
case let item as ReadMarkerRoomTimelineItem:
self = .readMarker(item, groupStyle)
case let item as PaginationIndicatorRoomTimelineItem:
self = .paginationIndicator(item, groupStyle)
case let item as StickerRoomTimelineItem:
self = .sticker(item, groupStyle)
case let item as UnsupportedRoomTimelineItem:
self = .unsupported(item, groupStyle)
case let item as TimelineStartRoomTimelineItem:
self = .timelineStart(item, groupStyle)
case let item as StateRoomTimelineItem:
self = .state(item, groupStyle)
case let item as CollapsibleTimelineItem:
self = .group(item, groupStyle)
case let item as LocationRoomTimelineItem:
self = .location(item, groupStyle)
default:
fatalError("Unknown timeline item")
}
}
var id: String {
switch self {
case .text(let item as RoomTimelineItemProtocol, _),
.separator(let item as RoomTimelineItemProtocol, _),
.image(let item as RoomTimelineItemProtocol, _),
.video(let item as RoomTimelineItemProtocol, _),
.audio(let item as RoomTimelineItemProtocol, _),
.file(let item as RoomTimelineItemProtocol, _),
.emote(let item as RoomTimelineItemProtocol, _),
.notice(let item as RoomTimelineItemProtocol, _),
.redacted(let item as RoomTimelineItemProtocol, _),
.encrypted(let item as RoomTimelineItemProtocol, _),
.readMarker(let item as RoomTimelineItemProtocol, _),
.paginationIndicator(let item as RoomTimelineItemProtocol, _),
.sticker(let item as RoomTimelineItemProtocol, _),
.unsupported(let item as RoomTimelineItemProtocol, _),
.timelineStart(let item as RoomTimelineItemProtocol, _),
.state(let item as RoomTimelineItemProtocol, _),
.group(let item as RoomTimelineItemProtocol, _),
.location(let item as RoomTimelineItemProtocol, _):
return item.id
}
}
/// Whether or not it is possible to send a reaction to this timeline item.
var isReactable: Bool {
switch self {
case .text, .image, .video, .audio, .file, .emote, .notice, .sticker, .location:
return true
case .redacted, .encrypted, .unsupported, .state: // Event based items that aren't reactable
return false
case .timelineStart, .separator, .readMarker, .paginationIndicator: // Virtual items are never reactable
return false
case .group:
return false
}
}
}
extension RoomTimelineViewProvider: View {
var body: some View {
timelineView
.environment(\.timelineGroupStyle, timelineGroupStyle)
}
@ViewBuilder private var timelineView: some View {
switch self {
case .text(let item, _):
TextRoomTimelineView(timelineItem: item)
case .separator(let item, _):
SeparatorRoomTimelineView(timelineItem: item)
case .image(let item, _):
ImageRoomTimelineView(timelineItem: item)
case .video(let item, _):
VideoRoomTimelineView(timelineItem: item)
case .audio(let item, _):
AudioRoomTimelineView(timelineItem: item)
case .file(let item, _):
FileRoomTimelineView(timelineItem: item)
case .emote(let item, _):
EmoteRoomTimelineView(timelineItem: item)
case .notice(let item, _):
NoticeRoomTimelineView(timelineItem: item)
case .redacted(let item, _):
RedactedRoomTimelineView(timelineItem: item)
case .encrypted(let item, _):
EncryptedRoomTimelineView(timelineItem: item)
case .readMarker(let item, _):
ReadMarkerRoomTimelineView(timelineItem: item)
case .paginationIndicator(let item, _):
PaginationIndicatorRoomTimelineView(timelineItem: item)
case .sticker(let item, _):
StickerRoomTimelineView(timelineItem: item)
case .unsupported(let item, _):
UnsupportedRoomTimelineView(timelineItem: item)
case .timelineStart(let item, _):
TimelineStartRoomTimelineView(timelineItem: item)
case .state(let item, _):
StateRoomTimelineView(timelineItem: item)
case .group(let item, _):
CollapsibleRoomTimelineView(timelineItem: item)
case .location(let item, _):
LocationRoomTimelineView(timelineItem: item)
}
}
var timelineGroupStyle: TimelineGroupStyle {
switch self {
case .text(_, let groupStyle),
.separator(_, let groupStyle),
.image(_, let groupStyle),
.video(_, let groupStyle),
.audio(_, let groupStyle),
.file(_, let groupStyle),
.emote(_, let groupStyle),
.notice(_, let groupStyle),
.redacted(_, let groupStyle),
.encrypted(_, let groupStyle),
.readMarker(_, let groupStyle),
.paginationIndicator(_, let groupStyle),
.sticker(_, let groupStyle),
.unsupported(_, let groupStyle),
.timelineStart(_, let groupStyle),
.state(_, let groupStyle),
.group(_, let groupStyle),
.location(_, let groupStyle):
return groupStyle
}
}
}