feat: add expires duration selector for poll
This commit is contained in:
parent
3eb2b916a7
commit
d05f97951b
|
@ -207,6 +207,15 @@
|
||||||
"attachment_broken": "This %s is broken and can't be\nuploaded to Mastodon.",
|
"attachment_broken": "This %s is broken and can't be\nuploaded to Mastodon.",
|
||||||
"description_photo": "Describe photo for low vision people...",
|
"description_photo": "Describe photo for low vision people...",
|
||||||
"description_video": "Describe what’s happening for low vision people..."
|
"description_video": "Describe what’s happening for low vision people..."
|
||||||
|
},
|
||||||
|
"poll": {
|
||||||
|
"duration_time": "Duration: %s",
|
||||||
|
"thirty_minutes": "30 minutes",
|
||||||
|
"one_hour": "1 Hour",
|
||||||
|
"six_hours": "6 Hours",
|
||||||
|
"one_day": "1 Day",
|
||||||
|
"three_days": "3 Days",
|
||||||
|
"seven_days": "7 Days"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -123,6 +123,7 @@
|
||||||
DB2B3ABC25E37E15007045F9 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = DB2B3ABE25E37E15007045F9 /* InfoPlist.strings */; };
|
DB2B3ABC25E37E15007045F9 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = DB2B3ABE25E37E15007045F9 /* InfoPlist.strings */; };
|
||||||
DB2B3AE925E38850007045F9 /* UIViewPreview.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB2B3AE825E38850007045F9 /* UIViewPreview.swift */; };
|
DB2B3AE925E38850007045F9 /* UIViewPreview.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB2B3AE825E38850007045F9 /* UIViewPreview.swift */; };
|
||||||
DB2F073525E8ECF000957B2D /* AuthenticationViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB2F073325E8ECF000957B2D /* AuthenticationViewModel.swift */; };
|
DB2F073525E8ECF000957B2D /* AuthenticationViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB2F073325E8ECF000957B2D /* AuthenticationViewModel.swift */; };
|
||||||
|
DB2FF510260B113300ADA9FE /* ComposeStatusPollExpiresOptionCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB2FF50F260B113300ADA9FE /* ComposeStatusPollExpiresOptionCollectionViewCell.swift */; };
|
||||||
DB3D0FF325BAA61700EAA174 /* AlamofireImage in Frameworks */ = {isa = PBXBuildFile; productRef = DB3D0FF225BAA61700EAA174 /* AlamofireImage */; };
|
DB3D0FF325BAA61700EAA174 /* AlamofireImage in Frameworks */ = {isa = PBXBuildFile; productRef = DB3D0FF225BAA61700EAA174 /* AlamofireImage */; };
|
||||||
DB3D100D25BAA75E00EAA174 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = DB3D100F25BAA75E00EAA174 /* Localizable.strings */; };
|
DB3D100D25BAA75E00EAA174 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = DB3D100F25BAA75E00EAA174 /* Localizable.strings */; };
|
||||||
DB427DD625BAA00100D1B89D /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB427DD525BAA00100D1B89D /* AppDelegate.swift */; };
|
DB427DD625BAA00100D1B89D /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB427DD525BAA00100D1B89D /* AppDelegate.swift */; };
|
||||||
|
@ -189,7 +190,7 @@
|
||||||
DB8190C62601FF0400020C08 /* AttachmentContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB8190C52601FF0400020C08 /* AttachmentContainerView.swift */; };
|
DB8190C62601FF0400020C08 /* AttachmentContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB8190C52601FF0400020C08 /* AttachmentContainerView.swift */; };
|
||||||
DB87D4452609BE0500D12C0D /* ComposeStatusPollOptionCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB87D4442609BE0500D12C0D /* ComposeStatusPollOptionCollectionViewCell.swift */; };
|
DB87D4452609BE0500D12C0D /* ComposeStatusPollOptionCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB87D4442609BE0500D12C0D /* ComposeStatusPollOptionCollectionViewCell.swift */; };
|
||||||
DB87D44B2609C11900D12C0D /* PollOptionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB87D44A2609C11900D12C0D /* PollOptionView.swift */; };
|
DB87D44B2609C11900D12C0D /* PollOptionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB87D44A2609C11900D12C0D /* PollOptionView.swift */; };
|
||||||
DB87D4512609CF1E00D12C0D /* ComposeStatusNewPollOptionCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB87D4502609CF1E00D12C0D /* ComposeStatusNewPollOptionCollectionViewCell.swift */; };
|
DB87D4512609CF1E00D12C0D /* ComposeStatusPollOptionAppendEntryCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB87D4502609CF1E00D12C0D /* ComposeStatusPollOptionAppendEntryCollectionViewCell.swift */; };
|
||||||
DB87D4572609DD5300D12C0D /* DeleteBackwardResponseTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB87D4562609DD5300D12C0D /* DeleteBackwardResponseTextField.swift */; };
|
DB87D4572609DD5300D12C0D /* DeleteBackwardResponseTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB87D4562609DD5300D12C0D /* DeleteBackwardResponseTextField.swift */; };
|
||||||
DB89B9F725C10FD0008580ED /* CoreDataStack.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DB89B9EE25C10FD0008580ED /* CoreDataStack.framework */; };
|
DB89B9F725C10FD0008580ED /* CoreDataStack.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DB89B9EE25C10FD0008580ED /* CoreDataStack.framework */; };
|
||||||
DB89B9FE25C10FD0008580ED /* CoreDataStackTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB89B9FD25C10FD0008580ED /* CoreDataStackTests.swift */; };
|
DB89B9FE25C10FD0008580ED /* CoreDataStackTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB89B9FD25C10FD0008580ED /* CoreDataStackTests.swift */; };
|
||||||
|
@ -418,6 +419,7 @@
|
||||||
DB2B3ABD25E37E15007045F9 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
DB2B3ABD25E37E15007045F9 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||||
DB2B3AE825E38850007045F9 /* UIViewPreview.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIViewPreview.swift; sourceTree = "<group>"; };
|
DB2B3AE825E38850007045F9 /* UIViewPreview.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIViewPreview.swift; sourceTree = "<group>"; };
|
||||||
DB2F073325E8ECF000957B2D /* AuthenticationViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AuthenticationViewModel.swift; sourceTree = "<group>"; };
|
DB2F073325E8ECF000957B2D /* AuthenticationViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AuthenticationViewModel.swift; sourceTree = "<group>"; };
|
||||||
|
DB2FF50F260B113300ADA9FE /* ComposeStatusPollExpiresOptionCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeStatusPollExpiresOptionCollectionViewCell.swift; sourceTree = "<group>"; };
|
||||||
DB3D0FED25BAA42200EAA174 /* MastodonSDK */ = {isa = PBXFileReference; lastKnownFileType = folder; path = MastodonSDK; sourceTree = "<group>"; };
|
DB3D0FED25BAA42200EAA174 /* MastodonSDK */ = {isa = PBXFileReference; lastKnownFileType = folder; path = MastodonSDK; sourceTree = "<group>"; };
|
||||||
DB3D100E25BAA75E00EAA174 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
|
DB3D100E25BAA75E00EAA174 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||||
DB427DD225BAA00100D1B89D /* Mastodon.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Mastodon.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
DB427DD225BAA00100D1B89D /* Mastodon.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Mastodon.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
@ -487,7 +489,7 @@
|
||||||
DB8190C52601FF0400020C08 /* AttachmentContainerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttachmentContainerView.swift; sourceTree = "<group>"; };
|
DB8190C52601FF0400020C08 /* AttachmentContainerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttachmentContainerView.swift; sourceTree = "<group>"; };
|
||||||
DB87D4442609BE0500D12C0D /* ComposeStatusPollOptionCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeStatusPollOptionCollectionViewCell.swift; sourceTree = "<group>"; };
|
DB87D4442609BE0500D12C0D /* ComposeStatusPollOptionCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeStatusPollOptionCollectionViewCell.swift; sourceTree = "<group>"; };
|
||||||
DB87D44A2609C11900D12C0D /* PollOptionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PollOptionView.swift; sourceTree = "<group>"; };
|
DB87D44A2609C11900D12C0D /* PollOptionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PollOptionView.swift; sourceTree = "<group>"; };
|
||||||
DB87D4502609CF1E00D12C0D /* ComposeStatusNewPollOptionCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeStatusNewPollOptionCollectionViewCell.swift; sourceTree = "<group>"; };
|
DB87D4502609CF1E00D12C0D /* ComposeStatusPollOptionAppendEntryCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeStatusPollOptionAppendEntryCollectionViewCell.swift; sourceTree = "<group>"; };
|
||||||
DB87D4562609DD5300D12C0D /* DeleteBackwardResponseTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeleteBackwardResponseTextField.swift; sourceTree = "<group>"; };
|
DB87D4562609DD5300D12C0D /* DeleteBackwardResponseTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeleteBackwardResponseTextField.swift; sourceTree = "<group>"; };
|
||||||
DB89B9EE25C10FD0008580ED /* CoreDataStack.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CoreDataStack.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
DB89B9EE25C10FD0008580ED /* CoreDataStack.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CoreDataStack.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
DB89B9F025C10FD0008580ED /* CoreDataStack.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CoreDataStack.h; sourceTree = "<group>"; };
|
DB89B9F025C10FD0008580ED /* CoreDataStack.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CoreDataStack.h; sourceTree = "<group>"; };
|
||||||
|
@ -1163,7 +1165,8 @@
|
||||||
DB789A1B25F9F76A0071ACA0 /* ComposeStatusContentCollectionViewCell.swift */,
|
DB789A1B25F9F76A0071ACA0 /* ComposeStatusContentCollectionViewCell.swift */,
|
||||||
DB6B351D2601FAEE00DC1E11 /* ComposeStatusAttachmentTableViewCell.swift */,
|
DB6B351D2601FAEE00DC1E11 /* ComposeStatusAttachmentTableViewCell.swift */,
|
||||||
DB87D4442609BE0500D12C0D /* ComposeStatusPollOptionCollectionViewCell.swift */,
|
DB87D4442609BE0500D12C0D /* ComposeStatusPollOptionCollectionViewCell.swift */,
|
||||||
DB87D4502609CF1E00D12C0D /* ComposeStatusNewPollOptionCollectionViewCell.swift */,
|
DB87D4502609CF1E00D12C0D /* ComposeStatusPollOptionAppendEntryCollectionViewCell.swift */,
|
||||||
|
DB2FF50F260B113300ADA9FE /* ComposeStatusPollExpiresOptionCollectionViewCell.swift */,
|
||||||
);
|
);
|
||||||
path = CollectionViewCell;
|
path = CollectionViewCell;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -1826,6 +1829,7 @@
|
||||||
DB68586425E619B700F0A850 /* NSKeyValueObservation.swift in Sources */,
|
DB68586425E619B700F0A850 /* NSKeyValueObservation.swift in Sources */,
|
||||||
2D61335825C188A000CAE157 /* APIService+Persist+Status.swift in Sources */,
|
2D61335825C188A000CAE157 /* APIService+Persist+Status.swift in Sources */,
|
||||||
DB45FAE325CA7181005A8AC7 /* MastodonUser.swift in Sources */,
|
DB45FAE325CA7181005A8AC7 /* MastodonUser.swift in Sources */,
|
||||||
|
DB2FF510260B113300ADA9FE /* ComposeStatusPollExpiresOptionCollectionViewCell.swift in Sources */,
|
||||||
DB0AC6FC25CD02E600D75117 /* APIService+Instance.swift in Sources */,
|
DB0AC6FC25CD02E600D75117 /* APIService+Instance.swift in Sources */,
|
||||||
2D69D00A25CAA00300C3A1B2 /* APIService+CoreData+Status.swift in Sources */,
|
2D69D00A25CAA00300C3A1B2 /* APIService+CoreData+Status.swift in Sources */,
|
||||||
DB4481C625EE2ADA00BEFB67 /* PollSection.swift in Sources */,
|
DB4481C625EE2ADA00BEFB67 /* PollSection.swift in Sources */,
|
||||||
|
@ -1953,7 +1957,7 @@
|
||||||
DB0140AE25C40C7300F9F3CF /* MastodonPinBasedAuthenticationViewModel.swift in Sources */,
|
DB0140AE25C40C7300F9F3CF /* MastodonPinBasedAuthenticationViewModel.swift in Sources */,
|
||||||
2D32EAAC25CB96DC00C9ED86 /* TimelineMiddleLoaderTableViewCell.swift in Sources */,
|
2D32EAAC25CB96DC00C9ED86 /* TimelineMiddleLoaderTableViewCell.swift in Sources */,
|
||||||
DB71FD2C25F86A5100512AE1 /* AvatarStackContainerButton.swift in Sources */,
|
DB71FD2C25F86A5100512AE1 /* AvatarStackContainerButton.swift in Sources */,
|
||||||
DB87D4512609CF1E00D12C0D /* ComposeStatusNewPollOptionCollectionViewCell.swift in Sources */,
|
DB87D4512609CF1E00D12C0D /* ComposeStatusPollOptionAppendEntryCollectionViewCell.swift in Sources */,
|
||||||
DB9A489026035963008B817C /* APIService+Media.swift in Sources */,
|
DB9A489026035963008B817C /* APIService+Media.swift in Sources */,
|
||||||
2D5A3D6225CFD9CB002347D6 /* HomeTimelineViewController+DebugAction.swift in Sources */,
|
2D5A3D6225CFD9CB002347D6 /* HomeTimelineViewController+DebugAction.swift in Sources */,
|
||||||
DB49A62525FF334C00B98345 /* EmojiService+CustomEmojiViewModel+LoadState.swift in Sources */,
|
DB49A62525FF334C00B98345 /* EmojiService+CustomEmojiViewModel+LoadState.swift in Sources */,
|
||||||
|
|
|
@ -14,8 +14,9 @@ enum ComposeStatusItem {
|
||||||
case replyTo(statusObjectID: NSManagedObjectID)
|
case replyTo(statusObjectID: NSManagedObjectID)
|
||||||
case input(replyToStatusObjectID: NSManagedObjectID?, attribute: ComposeStatusAttribute)
|
case input(replyToStatusObjectID: NSManagedObjectID?, attribute: ComposeStatusAttribute)
|
||||||
case attachment(attachmentService: MastodonAttachmentService)
|
case attachment(attachmentService: MastodonAttachmentService)
|
||||||
case poll(attribute: ComposePollAttribute)
|
case pollOption(attribute: ComposePollOptionAttribute)
|
||||||
case newPoll
|
case pollOptionAppendEntry
|
||||||
|
case pollExpiresOption(attribute: ComposePollExpiresOptionAttribute)
|
||||||
}
|
}
|
||||||
|
|
||||||
extension ComposeStatusItem: Equatable { }
|
extension ComposeStatusItem: Equatable { }
|
||||||
|
@ -44,16 +45,16 @@ extension ComposeStatusItem {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protocol ComposeStatusItemDelegate: class {
|
protocol ComposePollAttributeDelegate: class {
|
||||||
func composePollAttribute(_ attribute: ComposeStatusItem.ComposePollAttribute, pollOptionDidChange: String?)
|
func composePollAttribute(_ attribute: ComposeStatusItem.ComposePollOptionAttribute, pollOptionDidChange: String?)
|
||||||
}
|
}
|
||||||
|
|
||||||
extension ComposeStatusItem {
|
extension ComposeStatusItem {
|
||||||
final class ComposePollAttribute: Equatable, Hashable {
|
final class ComposePollOptionAttribute: Equatable, Hashable {
|
||||||
private let id = UUID()
|
private let id = UUID()
|
||||||
|
|
||||||
var disposeBag = Set<AnyCancellable>()
|
var disposeBag = Set<AnyCancellable>()
|
||||||
weak var delegate: ComposeStatusItemDelegate?
|
weak var delegate: ComposePollAttributeDelegate?
|
||||||
|
|
||||||
let option = CurrentValueSubject<String, Never>("")
|
let option = CurrentValueSubject<String, Never>("")
|
||||||
|
|
||||||
|
@ -70,7 +71,7 @@ extension ComposeStatusItem {
|
||||||
disposeBag.removeAll()
|
disposeBag.removeAll()
|
||||||
}
|
}
|
||||||
|
|
||||||
static func == (lhs: ComposePollAttribute, rhs: ComposePollAttribute) -> Bool {
|
static func == (lhs: ComposePollOptionAttribute, rhs: ComposePollOptionAttribute) -> Bool {
|
||||||
return lhs.id == rhs.id &&
|
return lhs.id == rhs.id &&
|
||||||
lhs.option.value == rhs.option.value
|
lhs.option.value == rhs.option.value
|
||||||
}
|
}
|
||||||
|
@ -80,3 +81,52 @@ extension ComposeStatusItem {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension ComposeStatusItem {
|
||||||
|
final class ComposePollExpiresOptionAttribute: Equatable, Hashable {
|
||||||
|
private let id = UUID()
|
||||||
|
|
||||||
|
let expiresOption = CurrentValueSubject<ExpiresOption, Never>(.thirtyMinutes)
|
||||||
|
|
||||||
|
|
||||||
|
static func == (lhs: ComposePollExpiresOptionAttribute, rhs: ComposePollExpiresOptionAttribute) -> Bool {
|
||||||
|
return lhs.id == rhs.id &&
|
||||||
|
lhs.expiresOption.value == rhs.expiresOption.value
|
||||||
|
}
|
||||||
|
|
||||||
|
func hash(into hasher: inout Hasher) {
|
||||||
|
hasher.combine(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ExpiresOption: Equatable, Hashable, CaseIterable {
|
||||||
|
case thirtyMinutes
|
||||||
|
case oneHour
|
||||||
|
case sixHours
|
||||||
|
case oneDay
|
||||||
|
case threeDays
|
||||||
|
case sevenDays
|
||||||
|
|
||||||
|
var title: String {
|
||||||
|
switch self {
|
||||||
|
case .thirtyMinutes: return L10n.Scene.Compose.Poll.thirtyMinutes
|
||||||
|
case .oneHour: return L10n.Scene.Compose.Poll.oneHour
|
||||||
|
case .sixHours: return L10n.Scene.Compose.Poll.sixHours
|
||||||
|
case .oneDay: return L10n.Scene.Compose.Poll.oneDay
|
||||||
|
case .threeDays: return L10n.Scene.Compose.Poll.threeDays
|
||||||
|
case .sevenDays: return L10n.Scene.Compose.Poll.sevenDays
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var seconds: Int {
|
||||||
|
switch self {
|
||||||
|
case .thirtyMinutes: return 60 * 30
|
||||||
|
case .oneHour: return 60 * 60 * 1
|
||||||
|
case .sixHours: return 60 * 60 * 6
|
||||||
|
case .oneDay: return 60 * 60 * 24
|
||||||
|
case .threeDays: return 60 * 60 * 24 * 3
|
||||||
|
case .sevenDays: return 60 * 60 * 24 * 7
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -36,7 +36,8 @@ extension ComposeStatusSection {
|
||||||
textEditorViewTextAttributesDelegate: TextEditorViewTextAttributesDelegate,
|
textEditorViewTextAttributesDelegate: TextEditorViewTextAttributesDelegate,
|
||||||
composeStatusAttachmentTableViewCellDelegate: ComposeStatusAttachmentCollectionViewCellDelegate,
|
composeStatusAttachmentTableViewCellDelegate: ComposeStatusAttachmentCollectionViewCellDelegate,
|
||||||
composeStatusPollOptionCollectionViewCellDelegate: ComposeStatusPollOptionCollectionViewCellDelegate,
|
composeStatusPollOptionCollectionViewCellDelegate: ComposeStatusPollOptionCollectionViewCellDelegate,
|
||||||
composeStatusNewPollOptionCollectionViewCellDelegate: ComposeStatusNewPollOptionCollectionViewCellDelegate
|
composeStatusNewPollOptionCollectionViewCellDelegate: ComposeStatusPollOptionAppendEntryCollectionViewCellDelegate,
|
||||||
|
composeStatusPollExpiresOptionCollectionViewCellDelegate: ComposeStatusPollExpiresOptionCollectionViewCellDelegate
|
||||||
) -> UICollectionViewDiffableDataSource<ComposeStatusSection, ComposeStatusItem> {
|
) -> UICollectionViewDiffableDataSource<ComposeStatusSection, ComposeStatusItem> {
|
||||||
UICollectionViewDiffableDataSource(collectionView: collectionView) { collectionView, indexPath, item -> UICollectionViewCell? in
|
UICollectionViewDiffableDataSource(collectionView: collectionView) { collectionView, indexPath, item -> UICollectionViewCell? in
|
||||||
switch item {
|
switch item {
|
||||||
|
@ -127,7 +128,7 @@ extension ComposeStatusSection {
|
||||||
}
|
}
|
||||||
.store(in: &cell.disposeBag)
|
.store(in: &cell.disposeBag)
|
||||||
return cell
|
return cell
|
||||||
case .poll(let attribute):
|
case .pollOption(let attribute):
|
||||||
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: String(describing: ComposeStatusPollOptionCollectionViewCell.self), for: indexPath) as! ComposeStatusPollOptionCollectionViewCell
|
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: String(describing: ComposeStatusPollOptionCollectionViewCell.self), for: indexPath) as! ComposeStatusPollOptionCollectionViewCell
|
||||||
cell.pollOptionView.optionTextField.text = attribute.option.value
|
cell.pollOptionView.optionTextField.text = attribute.option.value
|
||||||
cell.pollOption
|
cell.pollOption
|
||||||
|
@ -136,10 +137,21 @@ extension ComposeStatusSection {
|
||||||
.store(in: &cell.disposeBag)
|
.store(in: &cell.disposeBag)
|
||||||
cell.delegate = composeStatusPollOptionCollectionViewCellDelegate
|
cell.delegate = composeStatusPollOptionCollectionViewCellDelegate
|
||||||
return cell
|
return cell
|
||||||
case .newPoll:
|
case .pollOptionAppendEntry:
|
||||||
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: String(describing: ComposeStatusNewPollOptionCollectionViewCell.self), for: indexPath) as! ComposeStatusNewPollOptionCollectionViewCell
|
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: String(describing: ComposeStatusPollOptionAppendEntryCollectionViewCell.self), for: indexPath) as! ComposeStatusPollOptionAppendEntryCollectionViewCell
|
||||||
cell.delegate = composeStatusNewPollOptionCollectionViewCellDelegate
|
cell.delegate = composeStatusNewPollOptionCollectionViewCellDelegate
|
||||||
return cell
|
return cell
|
||||||
|
case .pollExpiresOption(let attribute):
|
||||||
|
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: String(describing: ComposeStatusPollExpiresOptionCollectionViewCell.self), for: indexPath) as! ComposeStatusPollExpiresOptionCollectionViewCell
|
||||||
|
cell.durationButton.setTitle(L10n.Scene.Compose.Poll.durationTime(attribute.expiresOption.value.title), for: .normal)
|
||||||
|
attribute.expiresOption
|
||||||
|
.receive(on: DispatchQueue.main)
|
||||||
|
.sink { expiresOption in
|
||||||
|
cell.durationButton.setTitle(L10n.Scene.Compose.Poll.durationTime(expiresOption.title), for: .normal)
|
||||||
|
}
|
||||||
|
.store(in: &cell.disposeBag)
|
||||||
|
cell.delegate = composeStatusPollExpiresOptionCollectionViewCellDelegate
|
||||||
|
return cell
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -170,6 +170,24 @@ internal enum L10n {
|
||||||
/// Photo Library
|
/// Photo Library
|
||||||
internal static let photoLibrary = L10n.tr("Localizable", "Scene.Compose.MediaSelection.PhotoLibrary")
|
internal static let photoLibrary = L10n.tr("Localizable", "Scene.Compose.MediaSelection.PhotoLibrary")
|
||||||
}
|
}
|
||||||
|
internal enum Poll {
|
||||||
|
/// Duration: %@
|
||||||
|
internal static func durationTime(_ p1: Any) -> String {
|
||||||
|
return L10n.tr("Localizable", "Scene.Compose.Poll.DurationTime", String(describing: p1))
|
||||||
|
}
|
||||||
|
/// 1 Day
|
||||||
|
internal static let oneDay = L10n.tr("Localizable", "Scene.Compose.Poll.OneDay")
|
||||||
|
/// 1 Hour
|
||||||
|
internal static let oneHour = L10n.tr("Localizable", "Scene.Compose.Poll.OneHour")
|
||||||
|
/// 7 Days
|
||||||
|
internal static let sevenDays = L10n.tr("Localizable", "Scene.Compose.Poll.SevenDays")
|
||||||
|
/// 6 Hours
|
||||||
|
internal static let sixHours = L10n.tr("Localizable", "Scene.Compose.Poll.SixHours")
|
||||||
|
/// 30 minutes
|
||||||
|
internal static let thirtyMinutes = L10n.tr("Localizable", "Scene.Compose.Poll.ThirtyMinutes")
|
||||||
|
/// 3 Days
|
||||||
|
internal static let threeDays = L10n.tr("Localizable", "Scene.Compose.Poll.ThreeDays")
|
||||||
|
}
|
||||||
internal enum Title {
|
internal enum Title {
|
||||||
/// New Post
|
/// New Post
|
||||||
internal static let newPost = L10n.tr("Localizable", "Scene.Compose.Title.NewPost")
|
internal static let newPost = L10n.tr("Localizable", "Scene.Compose.Title.NewPost")
|
||||||
|
|
|
@ -50,6 +50,13 @@ uploaded to Mastodon.";
|
||||||
"Scene.Compose.MediaSelection.Browse" = "Browse";
|
"Scene.Compose.MediaSelection.Browse" = "Browse";
|
||||||
"Scene.Compose.MediaSelection.Camera" = "Take Photo";
|
"Scene.Compose.MediaSelection.Camera" = "Take Photo";
|
||||||
"Scene.Compose.MediaSelection.PhotoLibrary" = "Photo Library";
|
"Scene.Compose.MediaSelection.PhotoLibrary" = "Photo Library";
|
||||||
|
"Scene.Compose.Poll.DurationTime" = "Duration: %@";
|
||||||
|
"Scene.Compose.Poll.OneDay" = "1 Day";
|
||||||
|
"Scene.Compose.Poll.OneHour" = "1 Hour";
|
||||||
|
"Scene.Compose.Poll.SevenDays" = "7 Days";
|
||||||
|
"Scene.Compose.Poll.SixHours" = "6 Hours";
|
||||||
|
"Scene.Compose.Poll.ThirtyMinutes" = "30 minutes";
|
||||||
|
"Scene.Compose.Poll.ThreeDays" = "3 Days";
|
||||||
"Scene.Compose.Title.NewPost" = "New Post";
|
"Scene.Compose.Title.NewPost" = "New Post";
|
||||||
"Scene.Compose.Title.NewReply" = "New Reply";
|
"Scene.Compose.Title.NewReply" = "New Reply";
|
||||||
"Scene.ConfirmEmail.Button.DontReceiveEmail" = "I never got an email";
|
"Scene.ConfirmEmail.Button.DontReceiveEmail" = "I never got an email";
|
||||||
|
|
|
@ -0,0 +1,74 @@
|
||||||
|
//
|
||||||
|
// ComposeStatusPollExpiresOptionCollectionViewCell.swift
|
||||||
|
// Mastodon
|
||||||
|
//
|
||||||
|
// Created by MainasuK Cirno on 2021-3-24.
|
||||||
|
//
|
||||||
|
|
||||||
|
import os.log
|
||||||
|
import UIKit
|
||||||
|
import Combine
|
||||||
|
|
||||||
|
protocol ComposeStatusPollExpiresOptionCollectionViewCellDelegate: class {
|
||||||
|
func composeStatusPollExpiresOptionCollectionViewCell(_ cell: ComposeStatusPollExpiresOptionCollectionViewCell, didSelectExpiresOption expiresOption: ComposeStatusItem.ComposePollExpiresOptionAttribute.ExpiresOption)
|
||||||
|
}
|
||||||
|
|
||||||
|
final class ComposeStatusPollExpiresOptionCollectionViewCell: UICollectionViewCell {
|
||||||
|
|
||||||
|
var disposeBag = Set<AnyCancellable>()
|
||||||
|
weak var delegate: ComposeStatusPollExpiresOptionCollectionViewCellDelegate?
|
||||||
|
|
||||||
|
let durationButton: UIButton = {
|
||||||
|
let button = HighlightDimmableButton()
|
||||||
|
button.titleLabel?.font = UIFontMetrics(forTextStyle: .body).scaledFont(for: .systemFont(ofSize: 12))
|
||||||
|
button.expandEdgeInsets = UIEdgeInsets(top: 0, left: -10, bottom: -20, right: -20)
|
||||||
|
button.setTitle(L10n.Scene.Compose.Poll.durationTime(L10n.Scene.Compose.Poll.thirtyMinutes), for: .normal)
|
||||||
|
button.setTitleColor(Asset.Colors.Button.normal.color, for: .normal)
|
||||||
|
return button
|
||||||
|
}()
|
||||||
|
|
||||||
|
override init(frame: CGRect) {
|
||||||
|
super.init(frame: frame)
|
||||||
|
_init()
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
super.init(coder: coder)
|
||||||
|
_init()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension ComposeStatusPollExpiresOptionCollectionViewCell {
|
||||||
|
|
||||||
|
private typealias ExpiresOption = ComposeStatusItem.ComposePollExpiresOptionAttribute.ExpiresOption
|
||||||
|
|
||||||
|
private func _init() {
|
||||||
|
durationButton.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
contentView.addSubview(durationButton)
|
||||||
|
NSLayoutConstraint.activate([
|
||||||
|
durationButton.topAnchor.constraint(equalTo: contentView.topAnchor),
|
||||||
|
durationButton.leadingAnchor.constraint(equalTo: contentView.readableContentGuide.leadingAnchor, constant: PollOptionView.checkmarkBackgroundLeadingMargin),
|
||||||
|
durationButton.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
|
||||||
|
])
|
||||||
|
|
||||||
|
let children = ExpiresOption.allCases.map { expiresOption -> UIAction in
|
||||||
|
UIAction(title: expiresOption.title, image: nil, identifier: nil, discoverabilityTitle: nil, attributes: [], state: .off) { [weak self] action in
|
||||||
|
guard let self = self else { return }
|
||||||
|
self.expiresOptionActionHandler(action, expiresOption: expiresOption)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
durationButton.menu = UIMenu(title: "", image: nil, identifier: nil, options: .displayInline, children: children)
|
||||||
|
durationButton.showsMenuAsPrimaryAction = true
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension ComposeStatusPollExpiresOptionCollectionViewCell {
|
||||||
|
|
||||||
|
private func expiresOptionActionHandler(_ sender: UIAction, expiresOption: ExpiresOption) {
|
||||||
|
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: select %s", ((#file as NSString).lastPathComponent), #line, #function, expiresOption.title)
|
||||||
|
delegate?.composeStatusPollExpiresOptionCollectionViewCell(self, didSelectExpiresOption: expiresOption)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
//
|
//
|
||||||
// ComposeStatusNewPollOptionCollectionViewCell.swift
|
// ComposeStatusPollOptionAppendEntryCollectionViewCell.swift
|
||||||
// Mastodon
|
// Mastodon
|
||||||
//
|
//
|
||||||
// Created by MainasuK Cirno on 2021-3-23.
|
// Created by MainasuK Cirno on 2021-3-23.
|
||||||
|
@ -8,11 +8,11 @@
|
||||||
import os.log
|
import os.log
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
protocol ComposeStatusNewPollOptionCollectionViewCellDelegate: class {
|
protocol ComposeStatusPollOptionAppendEntryCollectionViewCellDelegate: class {
|
||||||
func ComposeStatusNewPollOptionCollectionViewCellDidPressed(_ cell: ComposeStatusNewPollOptionCollectionViewCell)
|
func composeStatusPollOptionAppendEntryCollectionViewCellDidPressed(_ cell: ComposeStatusPollOptionAppendEntryCollectionViewCell)
|
||||||
}
|
}
|
||||||
|
|
||||||
final class ComposeStatusNewPollOptionCollectionViewCell: UICollectionViewCell {
|
final class ComposeStatusPollOptionAppendEntryCollectionViewCell: UICollectionViewCell {
|
||||||
|
|
||||||
let pollOptionView = PollOptionView()
|
let pollOptionView = PollOptionView()
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ final class ComposeStatusNewPollOptionCollectionViewCell: UICollectionViewCell {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
weak var delegate: ComposeStatusNewPollOptionCollectionViewCellDelegate?
|
weak var delegate: ComposeStatusPollOptionAppendEntryCollectionViewCellDelegate?
|
||||||
|
|
||||||
override func prepareForReuse() {
|
override func prepareForReuse() {
|
||||||
super.prepareForReuse()
|
super.prepareForReuse()
|
||||||
|
@ -45,7 +45,7 @@ final class ComposeStatusNewPollOptionCollectionViewCell: UICollectionViewCell {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension ComposeStatusNewPollOptionCollectionViewCell {
|
extension ComposeStatusPollOptionAppendEntryCollectionViewCell {
|
||||||
|
|
||||||
private func _init() {
|
private func _init() {
|
||||||
pollOptionView.translatesAutoresizingMaskIntoConstraints = false
|
pollOptionView.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
@ -67,7 +67,7 @@ extension ComposeStatusNewPollOptionCollectionViewCell {
|
||||||
setupBorderColor()
|
setupBorderColor()
|
||||||
|
|
||||||
pollOptionView.addGestureRecognizer(singleTagGestureRecognizer)
|
pollOptionView.addGestureRecognizer(singleTagGestureRecognizer)
|
||||||
singleTagGestureRecognizer.addTarget(self, action: #selector(ComposeStatusNewPollOptionCollectionViewCell.singleTagGestureRecognizerHandler(_:)))
|
singleTagGestureRecognizer.addTarget(self, action: #selector(ComposeStatusPollOptionAppendEntryCollectionViewCell.singleTagGestureRecognizerHandler(_:)))
|
||||||
}
|
}
|
||||||
|
|
||||||
private func setupBorderColor() {
|
private func setupBorderColor() {
|
||||||
|
@ -83,11 +83,11 @@ extension ComposeStatusNewPollOptionCollectionViewCell {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension ComposeStatusNewPollOptionCollectionViewCell {
|
extension ComposeStatusPollOptionAppendEntryCollectionViewCell {
|
||||||
|
|
||||||
@objc private func singleTagGestureRecognizerHandler(_ sender: UITapGestureRecognizer) {
|
@objc private func singleTagGestureRecognizerHandler(_ sender: UITapGestureRecognizer) {
|
||||||
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
|
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
|
||||||
delegate?.ComposeStatusNewPollOptionCollectionViewCellDidPressed(self)
|
delegate?.composeStatusPollOptionAppendEntryCollectionViewCellDidPressed(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -100,7 +100,7 @@ struct ComposeStatusNewPollOptionCollectionViewCell_Previews: PreviewProvider {
|
||||||
static var controls: some View {
|
static var controls: some View {
|
||||||
Group {
|
Group {
|
||||||
UIViewPreview() {
|
UIViewPreview() {
|
||||||
let cell = ComposeStatusNewPollOptionCollectionViewCell()
|
let cell = ComposeStatusPollOptionAppendEntryCollectionViewCell()
|
||||||
return cell
|
return cell
|
||||||
}
|
}
|
||||||
.previewLayout(.fixed(width: 375, height: 44 + 10))
|
.previewLayout(.fixed(width: 375, height: 44 + 10))
|
|
@ -46,7 +46,8 @@ final class ComposeViewController: UIViewController, NeedsDependency {
|
||||||
collectionView.register(ComposeStatusContentCollectionViewCell.self, forCellWithReuseIdentifier: String(describing: ComposeStatusContentCollectionViewCell.self))
|
collectionView.register(ComposeStatusContentCollectionViewCell.self, forCellWithReuseIdentifier: String(describing: ComposeStatusContentCollectionViewCell.self))
|
||||||
collectionView.register(ComposeStatusAttachmentCollectionViewCell.self, forCellWithReuseIdentifier: String(describing: ComposeStatusAttachmentCollectionViewCell.self))
|
collectionView.register(ComposeStatusAttachmentCollectionViewCell.self, forCellWithReuseIdentifier: String(describing: ComposeStatusAttachmentCollectionViewCell.self))
|
||||||
collectionView.register(ComposeStatusPollOptionCollectionViewCell.self, forCellWithReuseIdentifier: String(describing: ComposeStatusPollOptionCollectionViewCell.self))
|
collectionView.register(ComposeStatusPollOptionCollectionViewCell.self, forCellWithReuseIdentifier: String(describing: ComposeStatusPollOptionCollectionViewCell.self))
|
||||||
collectionView.register(ComposeStatusNewPollOptionCollectionViewCell.self, forCellWithReuseIdentifier: String(describing: ComposeStatusNewPollOptionCollectionViewCell.self))
|
collectionView.register(ComposeStatusPollOptionAppendEntryCollectionViewCell.self, forCellWithReuseIdentifier: String(describing: ComposeStatusPollOptionAppendEntryCollectionViewCell.self))
|
||||||
|
collectionView.register(ComposeStatusPollExpiresOptionCollectionViewCell.self, forCellWithReuseIdentifier: String(describing: ComposeStatusPollExpiresOptionCollectionViewCell.self))
|
||||||
collectionView.backgroundColor = Asset.Colors.Background.systemBackground.color
|
collectionView.backgroundColor = Asset.Colors.Background.systemBackground.color
|
||||||
return collectionView
|
return collectionView
|
||||||
}()
|
}()
|
||||||
|
@ -158,7 +159,8 @@ extension ComposeViewController {
|
||||||
textEditorViewTextAttributesDelegate: self,
|
textEditorViewTextAttributesDelegate: self,
|
||||||
composeStatusAttachmentTableViewCellDelegate: self,
|
composeStatusAttachmentTableViewCellDelegate: self,
|
||||||
composeStatusPollOptionCollectionViewCellDelegate: self,
|
composeStatusPollOptionCollectionViewCellDelegate: self,
|
||||||
composeStatusNewPollOptionCollectionViewCellDelegate: self
|
composeStatusNewPollOptionCollectionViewCellDelegate: self,
|
||||||
|
composeStatusPollExpiresOptionCollectionViewCellDelegate: self
|
||||||
)
|
)
|
||||||
|
|
||||||
// respond scrollView overlap change
|
// respond scrollView overlap change
|
||||||
|
@ -283,7 +285,7 @@ extension ComposeViewController {
|
||||||
}
|
}
|
||||||
|
|
||||||
private func pollOptionCollectionViewCell(of item: ComposeStatusItem) -> ComposeStatusPollOptionCollectionViewCell? {
|
private func pollOptionCollectionViewCell(of item: ComposeStatusItem) -> ComposeStatusPollOptionCollectionViewCell? {
|
||||||
guard case .poll = item else { return nil }
|
guard case .pollOption = item else { return nil }
|
||||||
guard let diffableDataSource = viewModel.diffableDataSource else { return nil }
|
guard let diffableDataSource = viewModel.diffableDataSource else { return nil }
|
||||||
guard let indexPath = diffableDataSource.indexPath(for: item),
|
guard let indexPath = diffableDataSource.indexPath(for: item),
|
||||||
let cell = collectionView.cellForItem(at: indexPath) as? ComposeStatusPollOptionCollectionViewCell else {
|
let cell = collectionView.cellForItem(at: indexPath) as? ComposeStatusPollOptionCollectionViewCell else {
|
||||||
|
@ -297,7 +299,7 @@ extension ComposeViewController {
|
||||||
guard let diffableDataSource = viewModel.diffableDataSource else { return nil }
|
guard let diffableDataSource = viewModel.diffableDataSource else { return nil }
|
||||||
let items = diffableDataSource.snapshot().itemIdentifiers(inSection: .poll)
|
let items = diffableDataSource.snapshot().itemIdentifiers(inSection: .poll)
|
||||||
let firstPollItem = items.first { item -> Bool in
|
let firstPollItem = items.first { item -> Bool in
|
||||||
guard case .poll = item else { return false }
|
guard case .pollOption = item else { return false }
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -312,7 +314,7 @@ extension ComposeViewController {
|
||||||
guard let diffableDataSource = viewModel.diffableDataSource else { return nil }
|
guard let diffableDataSource = viewModel.diffableDataSource else { return nil }
|
||||||
let items = diffableDataSource.snapshot().itemIdentifiers(inSection: .poll)
|
let items = diffableDataSource.snapshot().itemIdentifiers(inSection: .poll)
|
||||||
let lastPollItem = items.last { item -> Bool in
|
let lastPollItem = items.last { item -> Bool in
|
||||||
guard case .poll = item else { return false }
|
guard case .pollOption = item else { return false }
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -570,7 +572,7 @@ extension ComposeViewController: ComposeToolbarViewDelegate {
|
||||||
|
|
||||||
// setup initial poll option if needs
|
// setup initial poll option if needs
|
||||||
if viewModel.isPollComposing.value, viewModel.pollAttributes.value.isEmpty {
|
if viewModel.isPollComposing.value, viewModel.pollAttributes.value.isEmpty {
|
||||||
viewModel.pollAttributes.value = [ComposeStatusItem.ComposePollAttribute(), ComposeStatusItem.ComposePollAttribute()]
|
viewModel.pollAttributes.value = [ComposeStatusItem.ComposePollOptionAttribute(), ComposeStatusItem.ComposePollOptionAttribute()]
|
||||||
}
|
}
|
||||||
|
|
||||||
if viewModel.isPollComposing.value {
|
if viewModel.isPollComposing.value {
|
||||||
|
@ -704,7 +706,7 @@ extension ComposeViewController: ComposeStatusPollOptionCollectionViewCellDelega
|
||||||
guard let diffableDataSource = viewModel.diffableDataSource else { return }
|
guard let diffableDataSource = viewModel.diffableDataSource else { return }
|
||||||
guard let indexPath = collectionView.indexPath(for: cell) else { return }
|
guard let indexPath = collectionView.indexPath(for: cell) else { return }
|
||||||
guard let item = diffableDataSource.itemIdentifier(for: indexPath) else { return }
|
guard let item = diffableDataSource.itemIdentifier(for: indexPath) else { return }
|
||||||
guard case let .poll(attribute) = item else { return }
|
guard case let .pollOption(attribute) = item else { return }
|
||||||
|
|
||||||
var pollAttributes = viewModel.pollAttributes.value
|
var pollAttributes = viewModel.pollAttributes.value
|
||||||
guard let index = pollAttributes.firstIndex(of: attribute) else { return }
|
guard let index = pollAttributes.firstIndex(of: attribute) else { return }
|
||||||
|
@ -747,7 +749,7 @@ extension ComposeViewController: ComposeStatusPollOptionCollectionViewCellDelega
|
||||||
guard let diffableDataSource = viewModel.diffableDataSource else { return }
|
guard let diffableDataSource = viewModel.diffableDataSource else { return }
|
||||||
guard let indexPath = collectionView.indexPath(for: cell) else { return }
|
guard let indexPath = collectionView.indexPath(for: cell) else { return }
|
||||||
let pollItems = diffableDataSource.snapshot().itemIdentifiers(inSection: .poll).filter { item in
|
let pollItems = diffableDataSource.snapshot().itemIdentifiers(inSection: .poll).filter { item in
|
||||||
guard case .poll = item else { return false }
|
guard case .pollOption = item else { return false }
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
guard let item = diffableDataSource.itemIdentifier(for: indexPath) else { return }
|
guard let item = diffableDataSource.itemIdentifier(for: indexPath) else { return }
|
||||||
|
@ -770,12 +772,19 @@ extension ComposeViewController: ComposeStatusPollOptionCollectionViewCellDelega
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - ComposeStatusNewPollOptionCollectionViewCellDelegate
|
// MARK: - ComposeStatusPollOptionAppendEntryCollectionViewCellDelegate
|
||||||
extension ComposeViewController: ComposeStatusNewPollOptionCollectionViewCellDelegate {
|
extension ComposeViewController: ComposeStatusPollOptionAppendEntryCollectionViewCellDelegate {
|
||||||
func ComposeStatusNewPollOptionCollectionViewCellDidPressed(_ cell: ComposeStatusNewPollOptionCollectionViewCell) {
|
func composeStatusPollOptionAppendEntryCollectionViewCellDidPressed(_ cell: ComposeStatusPollOptionAppendEntryCollectionViewCell) {
|
||||||
viewModel.createNewPollOptionIfPossible()
|
viewModel.createNewPollOptionIfPossible()
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.markLastPollOptionCollectionViewCellBecomeFirstResponser()
|
self.markLastPollOptionCollectionViewCellBecomeFirstResponser()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - ComposeStatusPollExpiresOptionCollectionViewCellDelegate
|
||||||
|
extension ComposeViewController: ComposeStatusPollExpiresOptionCollectionViewCellDelegate {
|
||||||
|
func composeStatusPollExpiresOptionCollectionViewCell(_ cell: ComposeStatusPollExpiresOptionCollectionViewCell, didSelectExpiresOption expiresOption: ComposeStatusItem.ComposePollExpiresOptionAttribute.ExpiresOption) {
|
||||||
|
viewModel.pollExpiresOptionAttribute.expiresOption.value = expiresOption
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -16,7 +16,8 @@ extension ComposeViewModel {
|
||||||
textEditorViewTextAttributesDelegate: TextEditorViewTextAttributesDelegate,
|
textEditorViewTextAttributesDelegate: TextEditorViewTextAttributesDelegate,
|
||||||
composeStatusAttachmentTableViewCellDelegate: ComposeStatusAttachmentCollectionViewCellDelegate,
|
composeStatusAttachmentTableViewCellDelegate: ComposeStatusAttachmentCollectionViewCellDelegate,
|
||||||
composeStatusPollOptionCollectionViewCellDelegate: ComposeStatusPollOptionCollectionViewCellDelegate,
|
composeStatusPollOptionCollectionViewCellDelegate: ComposeStatusPollOptionCollectionViewCellDelegate,
|
||||||
composeStatusNewPollOptionCollectionViewCellDelegate: ComposeStatusNewPollOptionCollectionViewCellDelegate
|
composeStatusNewPollOptionCollectionViewCellDelegate: ComposeStatusPollOptionAppendEntryCollectionViewCellDelegate,
|
||||||
|
composeStatusPollExpiresOptionCollectionViewCellDelegate: ComposeStatusPollExpiresOptionCollectionViewCellDelegate
|
||||||
) {
|
) {
|
||||||
let diffableDataSource = ComposeStatusSection.collectionViewDiffableDataSource(
|
let diffableDataSource = ComposeStatusSection.collectionViewDiffableDataSource(
|
||||||
for: collectionView,
|
for: collectionView,
|
||||||
|
@ -26,7 +27,8 @@ extension ComposeViewModel {
|
||||||
textEditorViewTextAttributesDelegate: textEditorViewTextAttributesDelegate,
|
textEditorViewTextAttributesDelegate: textEditorViewTextAttributesDelegate,
|
||||||
composeStatusAttachmentTableViewCellDelegate: composeStatusAttachmentTableViewCellDelegate,
|
composeStatusAttachmentTableViewCellDelegate: composeStatusAttachmentTableViewCellDelegate,
|
||||||
composeStatusPollOptionCollectionViewCellDelegate: composeStatusPollOptionCollectionViewCellDelegate,
|
composeStatusPollOptionCollectionViewCellDelegate: composeStatusPollOptionCollectionViewCellDelegate,
|
||||||
composeStatusNewPollOptionCollectionViewCellDelegate: composeStatusNewPollOptionCollectionViewCellDelegate
|
composeStatusNewPollOptionCollectionViewCellDelegate: composeStatusNewPollOptionCollectionViewCellDelegate,
|
||||||
|
composeStatusPollExpiresOptionCollectionViewCellDelegate: composeStatusPollExpiresOptionCollectionViewCellDelegate
|
||||||
)
|
)
|
||||||
|
|
||||||
// Note: do not allow reorder due to the images display order following the upload time
|
// Note: do not allow reorder due to the images display order following the upload time
|
||||||
|
|
|
@ -52,7 +52,8 @@ final class ComposeViewModel {
|
||||||
let attachmentServices = CurrentValueSubject<[MastodonAttachmentService], Never>([])
|
let attachmentServices = CurrentValueSubject<[MastodonAttachmentService], Never>([])
|
||||||
|
|
||||||
// polls
|
// polls
|
||||||
let pollAttributes = CurrentValueSubject<[ComposeStatusItem.ComposePollAttribute], Never>([])
|
let pollAttributes = CurrentValueSubject<[ComposeStatusItem.ComposePollOptionAttribute], Never>([])
|
||||||
|
let pollExpiresOptionAttribute = ComposeStatusItem.ComposePollExpiresOptionAttribute()
|
||||||
|
|
||||||
init(
|
init(
|
||||||
context: AppContext,
|
context: AppContext,
|
||||||
|
@ -196,13 +197,14 @@ final class ComposeViewModel {
|
||||||
if isPollComposing {
|
if isPollComposing {
|
||||||
var pollItems: [ComposeStatusItem] = []
|
var pollItems: [ComposeStatusItem] = []
|
||||||
for pollAttribute in pollAttributes {
|
for pollAttribute in pollAttributes {
|
||||||
let item = ComposeStatusItem.poll(attribute: pollAttribute)
|
let item = ComposeStatusItem.pollOption(attribute: pollAttribute)
|
||||||
pollItems.append(item)
|
pollItems.append(item)
|
||||||
}
|
}
|
||||||
snapshot.appendItems(pollItems, toSection: .poll)
|
snapshot.appendItems(pollItems, toSection: .poll)
|
||||||
if pollAttributes.count < 4 {
|
if pollAttributes.count < 4 {
|
||||||
snapshot.appendItems([ComposeStatusItem.newPoll], toSection: .poll)
|
snapshot.appendItems([ComposeStatusItem.pollOptionAppendEntry], toSection: .poll)
|
||||||
}
|
}
|
||||||
|
snapshot.appendItems([ComposeStatusItem.pollExpiresOption(attribute: self.pollExpiresOptionAttribute)], toSection: .poll)
|
||||||
}
|
}
|
||||||
|
|
||||||
diffableDataSource.apply(snapshot)
|
diffableDataSource.apply(snapshot)
|
||||||
|
@ -268,7 +270,7 @@ extension ComposeViewModel {
|
||||||
func createNewPollOptionIfPossible() {
|
func createNewPollOptionIfPossible() {
|
||||||
guard pollAttributes.value.count < 4 else { return }
|
guard pollAttributes.value.count < 4 else { return }
|
||||||
|
|
||||||
let attribute = ComposeStatusItem.ComposePollAttribute()
|
let attribute = ComposeStatusItem.ComposePollOptionAttribute()
|
||||||
pollAttributes.value = pollAttributes.value + [attribute]
|
pollAttributes.value = pollAttributes.value + [attribute]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -281,9 +283,9 @@ extension ComposeViewModel: MastodonAttachmentServiceDelegate {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - ComposeStatusAttributeDelegate
|
// MARK: - ComposePollAttributeDelegate
|
||||||
extension ComposeViewModel: ComposeStatusItemDelegate {
|
extension ComposeViewModel: ComposePollAttributeDelegate {
|
||||||
func composePollAttribute(_ attribute: ComposeStatusItem.ComposePollAttribute, pollOptionDidChange: String?) {
|
func composePollAttribute(_ attribute: ComposeStatusItem.ComposePollOptionAttribute, pollOptionDidChange: String?) {
|
||||||
// trigger update
|
// trigger update
|
||||||
pollAttributes.value = pollAttributes.value
|
pollAttributes.value = pollAttributes.value
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ final class PollOptionView: UIView {
|
||||||
static let optionHeight: CGFloat = 44
|
static let optionHeight: CGFloat = 44
|
||||||
static let verticalMargin: CGFloat = 5
|
static let verticalMargin: CGFloat = 5
|
||||||
static let checkmarkImageSize = CGSize(width: 26, height: 26)
|
static let checkmarkImageSize = CGSize(width: 26, height: 26)
|
||||||
|
static let checkmarkBackgroundLeadingMargin: CGFloat = 9
|
||||||
|
|
||||||
private var viewStateDisposeBag = Set<AnyCancellable>()
|
private var viewStateDisposeBag = Set<AnyCancellable>()
|
||||||
|
|
||||||
|
@ -105,7 +106,7 @@ extension PollOptionView {
|
||||||
roundedBackgroundView.addSubview(checkmarkBackgroundView)
|
roundedBackgroundView.addSubview(checkmarkBackgroundView)
|
||||||
NSLayoutConstraint.activate([
|
NSLayoutConstraint.activate([
|
||||||
checkmarkBackgroundView.topAnchor.constraint(equalTo: roundedBackgroundView.topAnchor, constant: 9),
|
checkmarkBackgroundView.topAnchor.constraint(equalTo: roundedBackgroundView.topAnchor, constant: 9),
|
||||||
checkmarkBackgroundView.leadingAnchor.constraint(equalTo: roundedBackgroundView.leadingAnchor, constant: 9),
|
checkmarkBackgroundView.leadingAnchor.constraint(equalTo: roundedBackgroundView.leadingAnchor, constant: PollOptionView.checkmarkBackgroundLeadingMargin),
|
||||||
roundedBackgroundView.bottomAnchor.constraint(equalTo: checkmarkBackgroundView.bottomAnchor, constant: 9),
|
roundedBackgroundView.bottomAnchor.constraint(equalTo: checkmarkBackgroundView.bottomAnchor, constant: 9),
|
||||||
checkmarkBackgroundView.widthAnchor.constraint(equalToConstant: PollOptionView.checkmarkImageSize.width).priority(.required - 1),
|
checkmarkBackgroundView.widthAnchor.constraint(equalToConstant: PollOptionView.checkmarkImageSize.width).priority(.required - 1),
|
||||||
checkmarkBackgroundView.heightAnchor.constraint(equalToConstant: PollOptionView.checkmarkImageSize.height).priority(.required - 1),
|
checkmarkBackgroundView.heightAnchor.constraint(equalToConstant: PollOptionView.checkmarkImageSize.height).priority(.required - 1),
|
||||||
|
|
Loading…
Reference in New Issue