feat: [WIP] restore compose poll view
This commit is contained in:
parent
f1b5c52815
commit
44a8b818e4
|
@ -106,30 +106,7 @@ final class ComposeViewController: UIViewController, NeedsDependency {
|
|||
// var composeToolbarViewBottomLayoutConstraint: NSLayoutConstraint!
|
||||
// let composeToolbarBackgroundView = UIView()
|
||||
//
|
||||
// static func createPhotoLibraryPickerConfiguration(selectionLimit: Int = 4) -> PHPickerConfiguration {
|
||||
// var configuration = PHPickerConfiguration()
|
||||
// configuration.filter = .any(of: [.images, .videos])
|
||||
// configuration.selectionLimit = selectionLimit
|
||||
// return configuration
|
||||
// }
|
||||
//
|
||||
// private(set) lazy var photoLibraryPicker: PHPickerViewController = {
|
||||
// let imagePicker = PHPickerViewController(configuration: ComposeViewController.createPhotoLibraryPickerConfiguration())
|
||||
// imagePicker.delegate = self
|
||||
// return imagePicker
|
||||
// }()
|
||||
// private(set) lazy var imagePickerController: UIImagePickerController = {
|
||||
// let imagePickerController = UIImagePickerController()
|
||||
// imagePickerController.sourceType = .camera
|
||||
// imagePickerController.delegate = self
|
||||
// return imagePickerController
|
||||
// }()
|
||||
//
|
||||
// private(set) lazy var documentPickerController: UIDocumentPickerViewController = {
|
||||
// let documentPickerController = UIDocumentPickerViewController(forOpeningContentTypes: [.image, .movie])
|
||||
// documentPickerController.delegate = self
|
||||
// return documentPickerController
|
||||
// }()
|
||||
|
||||
//
|
||||
// private(set) lazy var autoCompleteViewController: AutoCompleteViewController = {
|
||||
// let viewController = AutoCompleteViewController()
|
||||
|
@ -814,29 +791,7 @@ extension ComposeViewController {
|
|||
//
|
||||
//// MARK: - ComposeToolbarViewDelegate
|
||||
//extension ComposeViewController: ComposeToolbarViewDelegate {
|
||||
//
|
||||
// func composeToolbarView(_ composeToolbarView: ComposeToolbarView, mediaButtonDidPressed sender: Any, mediaSelectionType type: ComposeToolbarView.MediaSelectionType) {
|
||||
// switch type {
|
||||
// case .photoLibrary:
|
||||
// present(photoLibraryPicker, animated: true, completion: nil)
|
||||
// case .camera:
|
||||
// present(imagePickerController, animated: true, completion: nil)
|
||||
// case .browse:
|
||||
// #if SNAPSHOT
|
||||
// guard let image = UIImage(named: "Athens") else { return }
|
||||
//
|
||||
// let attachmentService = MastodonAttachmentService(
|
||||
// context: context,
|
||||
// image: image,
|
||||
// initialAuthenticationBox: viewModel.authenticationBox
|
||||
// )
|
||||
// viewModel.attachmentServices = viewModel.attachmentServices + [attachmentService]
|
||||
// #else
|
||||
// present(documentPickerController, animated: true, completion: nil)
|
||||
// #endif
|
||||
// }
|
||||
// }
|
||||
//
|
||||
|
||||
// func composeToolbarView(_ composeToolbarView: ComposeToolbarView, pollButtonDidPressed sender: Any) {
|
||||
// // toggle poll composing state
|
||||
// viewModel.isPollComposing.toggle()
|
||||
|
@ -943,59 +898,6 @@ extension ComposeViewController: UIAdaptivePresentationControllerDelegate {
|
|||
|
||||
}
|
||||
|
||||
//// MARK: - PHPickerViewControllerDelegate
|
||||
//extension ComposeViewController: PHPickerViewControllerDelegate {
|
||||
// func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
|
||||
// picker.dismiss(animated: true, completion: nil)
|
||||
//
|
||||
// let attachmentServices: [MastodonAttachmentService] = results.map { result in
|
||||
// let service = MastodonAttachmentService(
|
||||
// context: context,
|
||||
// pickerResult: result,
|
||||
// initialAuthenticationBox: viewModel.authenticationBox
|
||||
// )
|
||||
// return service
|
||||
// }
|
||||
// viewModel.attachmentServices = viewModel.attachmentServices + attachmentServices
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//// MARK: - UIImagePickerControllerDelegate
|
||||
//extension ComposeViewController: UIImagePickerControllerDelegate & UINavigationControllerDelegate {
|
||||
//
|
||||
// func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
|
||||
// picker.dismiss(animated: true, completion: nil)
|
||||
//
|
||||
// guard let image = info[.originalImage] as? UIImage else { return }
|
||||
//
|
||||
// let attachmentService = MastodonAttachmentService(
|
||||
// context: context,
|
||||
// image: image,
|
||||
// initialAuthenticationBox: viewModel.authenticationBox
|
||||
// )
|
||||
// viewModel.attachmentServices = viewModel.attachmentServices + [attachmentService]
|
||||
// }
|
||||
//
|
||||
// func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
|
||||
// os_log("%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
|
||||
// picker.dismiss(animated: true, completion: nil)
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//// MARK: - UIDocumentPickerDelegate
|
||||
//extension ComposeViewController: UIDocumentPickerDelegate {
|
||||
// func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
|
||||
// guard let url = urls.first else { return }
|
||||
//
|
||||
// let attachmentService = MastodonAttachmentService(
|
||||
// context: context,
|
||||
// documentURL: url,
|
||||
// initialAuthenticationBox: viewModel.authenticationBox
|
||||
// )
|
||||
// viewModel.attachmentServices = viewModel.attachmentServices + [attachmentService]
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//// MARK: - ComposeStatusAttachmentTableViewCellDelegate
|
||||
//extension ComposeViewController: ComposeStatusAttachmentCollectionViewCellDelegate {
|
||||
//
|
||||
|
|
15
MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Scene/Compose/Mention.imageset/Contents.json
vendored
Normal file
15
MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Scene/Compose/Mention.imageset/Contents.json
vendored
Normal file
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "Mention.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
},
|
||||
"properties" : {
|
||||
"preserves-vector-representation" : true
|
||||
}
|
||||
}
|
102
MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Scene/Compose/Mention.imageset/Mention.pdf
vendored
Normal file
102
MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Scene/Compose/Mention.imageset/Mention.pdf
vendored
Normal file
|
@ -0,0 +1,102 @@
|
|||
%PDF-1.7
|
||||
|
||||
1 0 obj
|
||||
<< >>
|
||||
endobj
|
||||
|
||||
2 0 obj
|
||||
<< /Length 3 0 R >>
|
||||
stream
|
||||
/DeviceRGB CS
|
||||
/DeviceRGB cs
|
||||
q
|
||||
1.000000 0.000000 -0.000000 1.000000 2.000000 2.000000 cm
|
||||
0.000000 0.000000 0.000000 scn
|
||||
20.000000 10.000000 m
|
||||
20.000000 8.250000 l
|
||||
20.000000 6.178932 18.321068 4.500000 16.250000 4.500000 c
|
||||
14.745829 4.500000 13.448502 5.385603 12.850986 6.663840 c
|
||||
12.032894 5.644792 10.840015 5.000000 9.500000 5.000000 c
|
||||
6.992370 5.000000 5.000000 7.258018 5.000000 10.000000 c
|
||||
5.000000 12.741982 6.992370 15.000000 9.500000 15.000000 c
|
||||
10.659005 15.000000 11.707939 14.517641 12.500963 13.728080 c
|
||||
12.500000 14.250000 l
|
||||
12.500000 14.664213 12.835787 15.000000 13.250000 15.000000 c
|
||||
13.629696 15.000000 13.943491 14.717846 13.993154 14.351770 c
|
||||
14.000000 14.250000 l
|
||||
14.000000 8.250000 l
|
||||
14.000000 7.007360 15.007360 6.000000 16.250000 6.000000 c
|
||||
17.440865 6.000000 18.415646 6.925161 18.494810 8.095951 c
|
||||
18.500000 8.250000 l
|
||||
18.500000 10.000000 l
|
||||
18.500000 14.694420 14.694420 18.500000 10.000000 18.500000 c
|
||||
5.305580 18.500000 1.500000 14.694420 1.500000 10.000000 c
|
||||
1.500000 5.305580 5.305580 1.500000 10.000000 1.500000 c
|
||||
11.032966 1.500000 12.039467 1.683977 12.985156 2.038752 c
|
||||
13.372977 2.184242 13.805312 1.987795 13.950803 1.599974 c
|
||||
14.096293 1.212152 13.899846 0.779818 13.512025 0.634327 c
|
||||
12.398500 0.216589 11.213587 0.000000 10.000000 0.000000 c
|
||||
4.477152 0.000000 0.000000 4.477152 0.000000 10.000000 c
|
||||
0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c
|
||||
15.429239 20.000000 19.847933 15.673328 19.996159 10.279904 c
|
||||
20.000000 10.000000 l
|
||||
20.000000 8.250000 l
|
||||
20.000000 10.000000 l
|
||||
h
|
||||
9.500000 13.500000 m
|
||||
7.865495 13.500000 6.500000 11.952439 6.500000 10.000000 c
|
||||
6.500000 8.047561 7.865495 6.500000 9.500000 6.500000 c
|
||||
11.134505 6.500000 12.500000 8.047561 12.500000 10.000000 c
|
||||
12.500000 11.952439 11.134505 13.500000 9.500000 13.500000 c
|
||||
h
|
||||
f
|
||||
n
|
||||
Q
|
||||
|
||||
endstream
|
||||
endobj
|
||||
|
||||
3 0 obj
|
||||
1791
|
||||
endobj
|
||||
|
||||
4 0 obj
|
||||
<< /Annots []
|
||||
/Type /Page
|
||||
/MediaBox [ 0.000000 0.000000 24.000000 24.000000 ]
|
||||
/Resources 1 0 R
|
||||
/Contents 2 0 R
|
||||
/Parent 5 0 R
|
||||
>>
|
||||
endobj
|
||||
|
||||
5 0 obj
|
||||
<< /Kids [ 4 0 R ]
|
||||
/Count 1
|
||||
/Type /Pages
|
||||
>>
|
||||
endobj
|
||||
|
||||
6 0 obj
|
||||
<< /Pages 5 0 R
|
||||
/Type /Catalog
|
||||
>>
|
||||
endobj
|
||||
|
||||
xref
|
||||
0 7
|
||||
0000000000 65535 f
|
||||
0000000010 00000 n
|
||||
0000000034 00000 n
|
||||
0000001881 00000 n
|
||||
0000001904 00000 n
|
||||
0000002077 00000 n
|
||||
0000002151 00000 n
|
||||
trailer
|
||||
<< /ID [ (some) (id) ]
|
||||
/Root 6 0 R
|
||||
/Size 7
|
||||
>>
|
||||
startxref
|
||||
2210
|
||||
%%EOF
|
15
MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Scene/Compose/More.imageset/Contents.json
vendored
Normal file
15
MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Scene/Compose/More.imageset/Contents.json
vendored
Normal file
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "More.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
},
|
||||
"properties" : {
|
||||
"preserves-vector-representation" : true
|
||||
}
|
||||
}
|
83
MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Scene/Compose/More.imageset/More.pdf
vendored
Normal file
83
MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Scene/Compose/More.imageset/More.pdf
vendored
Normal file
|
@ -0,0 +1,83 @@
|
|||
%PDF-1.7
|
||||
|
||||
1 0 obj
|
||||
<< >>
|
||||
endobj
|
||||
|
||||
2 0 obj
|
||||
<< /Length 3 0 R >>
|
||||
stream
|
||||
/DeviceRGB CS
|
||||
/DeviceRGB cs
|
||||
q
|
||||
1.000000 0.000000 -0.000000 1.000000 4.250000 10.250000 cm
|
||||
0.000000 0.000000 0.000000 scn
|
||||
3.500000 1.750000 m
|
||||
3.500000 0.783502 2.716498 0.000000 1.750000 0.000000 c
|
||||
0.783502 0.000000 0.000000 0.783502 0.000000 1.750000 c
|
||||
0.000000 2.716498 0.783502 3.500000 1.750000 3.500000 c
|
||||
2.716498 3.500000 3.500000 2.716498 3.500000 1.750000 c
|
||||
h
|
||||
9.500000 1.750000 m
|
||||
9.500000 0.783502 8.716498 0.000000 7.750000 0.000000 c
|
||||
6.783502 0.000000 6.000000 0.783502 6.000000 1.750000 c
|
||||
6.000000 2.716498 6.783502 3.500000 7.750000 3.500000 c
|
||||
8.716498 3.500000 9.500000 2.716498 9.500000 1.750000 c
|
||||
h
|
||||
13.750000 0.000000 m
|
||||
14.716498 0.000000 15.500000 0.783502 15.500000 1.750000 c
|
||||
15.500000 2.716498 14.716498 3.500000 13.750000 3.500000 c
|
||||
12.783502 3.500000 12.000000 2.716498 12.000000 1.750000 c
|
||||
12.000000 0.783502 12.783502 0.000000 13.750000 0.000000 c
|
||||
h
|
||||
f
|
||||
n
|
||||
Q
|
||||
|
||||
endstream
|
||||
endobj
|
||||
|
||||
3 0 obj
|
||||
877
|
||||
endobj
|
||||
|
||||
4 0 obj
|
||||
<< /Annots []
|
||||
/Type /Page
|
||||
/MediaBox [ 0.000000 0.000000 24.000000 24.000000 ]
|
||||
/Resources 1 0 R
|
||||
/Contents 2 0 R
|
||||
/Parent 5 0 R
|
||||
>>
|
||||
endobj
|
||||
|
||||
5 0 obj
|
||||
<< /Kids [ 4 0 R ]
|
||||
/Count 1
|
||||
/Type /Pages
|
||||
>>
|
||||
endobj
|
||||
|
||||
6 0 obj
|
||||
<< /Pages 5 0 R
|
||||
/Type /Catalog
|
||||
>>
|
||||
endobj
|
||||
|
||||
xref
|
||||
0 7
|
||||
0000000000 65535 f
|
||||
0000000010 00000 n
|
||||
0000000034 00000 n
|
||||
0000000967 00000 n
|
||||
0000000989 00000 n
|
||||
0000001162 00000 n
|
||||
0000001236 00000 n
|
||||
trailer
|
||||
<< /ID [ (some) (id) ]
|
||||
/Root 6 0 R
|
||||
/Size 7
|
||||
>>
|
||||
startxref
|
||||
1295
|
||||
%%EOF
|
15
MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Scene/Compose/People.imageset/Contents.json
vendored
Normal file
15
MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Scene/Compose/People.imageset/Contents.json
vendored
Normal file
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "People.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
},
|
||||
"properties" : {
|
||||
"preserves-vector-representation" : true
|
||||
}
|
||||
}
|
140
MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Scene/Compose/People.imageset/People.pdf
vendored
Normal file
140
MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Scene/Compose/People.imageset/People.pdf
vendored
Normal file
|
@ -0,0 +1,140 @@
|
|||
%PDF-1.7
|
||||
|
||||
1 0 obj
|
||||
<< >>
|
||||
endobj
|
||||
|
||||
2 0 obj
|
||||
<< /Length 3 0 R >>
|
||||
stream
|
||||
/DeviceRGB CS
|
||||
/DeviceRGB cs
|
||||
q
|
||||
1.000000 0.000000 -0.000000 1.000000 2.000000 2.000000 cm
|
||||
0.000000 0.000000 0.000000 scn
|
||||
2.000000 8.001000 m
|
||||
11.000000 8.000000 l
|
||||
12.053818 8.000000 12.918116 7.184515 12.994511 6.149316 c
|
||||
13.000000 6.000000 l
|
||||
13.000000 4.500000 l
|
||||
12.999000 1.000000 9.284000 0.000000 6.500000 0.000000 c
|
||||
3.777867 0.000000 0.164695 0.956049 0.005454 4.270353 c
|
||||
0.000000 4.500000 l
|
||||
0.000000 6.001000 l
|
||||
0.000000 7.054819 0.816397 7.919116 1.850808 7.995511 c
|
||||
2.000000 8.001000 l
|
||||
h
|
||||
13.220000 8.000000 m
|
||||
18.000000 8.000000 l
|
||||
19.053818 8.000000 19.918116 7.183603 19.994511 6.149192 c
|
||||
20.000000 6.000000 l
|
||||
20.000000 5.000000 l
|
||||
19.999001 1.938000 17.142000 1.000000 15.000000 1.000000 c
|
||||
14.320000 1.000000 13.568999 1.096001 12.860000 1.322001 c
|
||||
13.196000 1.708000 13.467000 2.149000 13.662000 2.649000 c
|
||||
14.205000 2.524000 14.715000 2.500000 15.000000 2.500000 c
|
||||
15.266544 2.505959 l
|
||||
16.251810 2.549091 18.352863 2.869398 18.492661 4.795017 c
|
||||
18.500000 5.000000 l
|
||||
18.500000 6.000000 l
|
||||
18.500000 6.245334 18.322222 6.449580 18.089575 6.491940 c
|
||||
18.000000 6.500000 l
|
||||
13.949000 6.500000 l
|
||||
13.865001 7.001375 13.655437 7.456812 13.354479 7.840185 c
|
||||
13.220000 8.000000 l
|
||||
18.000000 8.000000 l
|
||||
13.220000 8.000000 l
|
||||
h
|
||||
2.000000 6.501000 m
|
||||
1.899344 6.491000 l
|
||||
1.774960 6.465720 1.690000 6.398199 1.646000 6.355000 c
|
||||
1.602800 6.311000 1.535280 6.226681 1.510000 6.102040 c
|
||||
1.500000 6.001000 l
|
||||
1.500000 4.500000 l
|
||||
1.500000 3.491000 1.950000 2.778000 2.917000 2.257999 c
|
||||
3.743154 1.813076 4.919508 1.543680 6.182578 1.504868 c
|
||||
6.500000 1.500000 l
|
||||
6.817405 1.504868 l
|
||||
8.080349 1.543680 9.255923 1.813076 10.083000 2.257999 c
|
||||
10.988626 2.745499 11.441613 3.402630 11.494699 4.315081 c
|
||||
11.500000 4.500999 l
|
||||
11.500000 6.000000 l
|
||||
11.500000 6.245334 11.322222 6.449580 11.089575 6.491940 c
|
||||
11.000000 6.500000 l
|
||||
2.000000 6.501000 l
|
||||
h
|
||||
6.500000 19.000000 m
|
||||
8.985000 19.000000 11.000000 16.985001 11.000000 14.500000 c
|
||||
11.000000 12.015000 8.985000 10.000000 6.500000 10.000000 c
|
||||
4.015000 10.000000 2.000000 12.015000 2.000000 14.500000 c
|
||||
2.000000 16.985001 4.015000 19.000000 6.500000 19.000000 c
|
||||
h
|
||||
15.500000 17.000000 m
|
||||
17.433001 17.000000 19.000000 15.433001 19.000000 13.500000 c
|
||||
19.000000 11.566999 17.433001 10.000000 15.500000 10.000000 c
|
||||
13.567000 10.000000 12.000000 11.566999 12.000000 13.500000 c
|
||||
12.000000 15.433001 13.567000 17.000000 15.500000 17.000000 c
|
||||
h
|
||||
6.500000 17.500000 m
|
||||
4.846000 17.500000 3.500000 16.153999 3.500000 14.500000 c
|
||||
3.500000 12.846000 4.846000 11.500000 6.500000 11.500000 c
|
||||
8.154000 11.500000 9.500000 12.846000 9.500000 14.500000 c
|
||||
9.500000 16.153999 8.154000 17.500000 6.500000 17.500000 c
|
||||
h
|
||||
15.500000 15.500000 m
|
||||
14.397000 15.500000 13.500000 14.603001 13.500000 13.500000 c
|
||||
13.500000 12.396999 14.397000 11.500000 15.500000 11.500000 c
|
||||
16.603001 11.500000 17.500000 12.396999 17.500000 13.500000 c
|
||||
17.500000 14.603001 16.603001 15.500000 15.500000 15.500000 c
|
||||
h
|
||||
f
|
||||
n
|
||||
Q
|
||||
|
||||
endstream
|
||||
endobj
|
||||
|
||||
3 0 obj
|
||||
2893
|
||||
endobj
|
||||
|
||||
4 0 obj
|
||||
<< /Annots []
|
||||
/Type /Page
|
||||
/MediaBox [ 0.000000 0.000000 24.000000 24.000000 ]
|
||||
/Resources 1 0 R
|
||||
/Contents 2 0 R
|
||||
/Parent 5 0 R
|
||||
>>
|
||||
endobj
|
||||
|
||||
5 0 obj
|
||||
<< /Kids [ 4 0 R ]
|
||||
/Count 1
|
||||
/Type /Pages
|
||||
>>
|
||||
endobj
|
||||
|
||||
6 0 obj
|
||||
<< /Pages 5 0 R
|
||||
/Type /Catalog
|
||||
>>
|
||||
endobj
|
||||
|
||||
xref
|
||||
0 7
|
||||
0000000000 65535 f
|
||||
0000000010 00000 n
|
||||
0000000034 00000 n
|
||||
0000002983 00000 n
|
||||
0000003006 00000 n
|
||||
0000003179 00000 n
|
||||
0000003253 00000 n
|
||||
trailer
|
||||
<< /ID [ (some) (id) ]
|
||||
/Root 6 0 R
|
||||
/Size 7
|
||||
>>
|
||||
startxref
|
||||
3312
|
||||
%%EOF
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "chat.warning.fill.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
},
|
||||
"properties" : {
|
||||
"preserves-vector-representation" : true
|
||||
}
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
%PDF-1.7
|
||||
|
||||
1 0 obj
|
||||
<< >>
|
||||
endobj
|
||||
|
||||
2 0 obj
|
||||
<< /Length 3 0 R >>
|
||||
stream
|
||||
/DeviceRGB CS
|
||||
/DeviceRGB cs
|
||||
q
|
||||
1.000000 0.000000 -0.000000 1.000000 2.000000 1.907806 cm
|
||||
0.000000 0.000000 0.000000 scn
|
||||
20.000000 10.093658 m
|
||||
20.000000 15.616507 15.522848 20.093658 10.000000 20.093658 c
|
||||
4.477152 20.093658 0.000000 15.616507 0.000000 10.093658 c
|
||||
0.000000 8.450871 0.397199 6.864305 1.144898 5.443409 c
|
||||
0.028547 1.155022 l
|
||||
-0.008018 1.014606 -0.008014 0.867102 0.028576 0.726627 c
|
||||
0.146904 0.272343 0.611098 -0.000004 1.065382 0.118324 c
|
||||
5.355775 1.235390 l
|
||||
6.775161 0.489723 8.359558 0.093658 10.000000 0.093658 c
|
||||
15.522848 0.093658 20.000000 4.570810 20.000000 10.093658 c
|
||||
h
|
||||
10.000000 15.592194 m
|
||||
10.414213 15.592194 10.750000 15.256407 10.750000 14.842194 c
|
||||
10.750000 8.592194 l
|
||||
10.750000 8.177980 10.414213 7.842194 10.000000 7.842194 c
|
||||
9.585787 7.842194 9.250000 8.177980 9.250000 8.592194 c
|
||||
9.250000 14.842194 l
|
||||
9.250000 15.256407 9.585787 15.592194 10.000000 15.592194 c
|
||||
h
|
||||
11.000000 5.594330 m
|
||||
11.000000 5.042046 10.552285 4.594330 10.000000 4.594330 c
|
||||
9.447715 4.594330 9.000000 5.042046 9.000000 5.594330 c
|
||||
9.000000 6.146615 9.447715 6.594330 10.000000 6.594330 c
|
||||
10.552285 6.594330 11.000000 6.146615 11.000000 5.594330 c
|
||||
h
|
||||
f
|
||||
n
|
||||
Q
|
||||
|
||||
endstream
|
||||
endobj
|
||||
|
||||
3 0 obj
|
||||
1155
|
||||
endobj
|
||||
|
||||
4 0 obj
|
||||
<< /Annots []
|
||||
/Type /Page
|
||||
/MediaBox [ 0.000000 0.000000 24.000000 24.000000 ]
|
||||
/Resources 1 0 R
|
||||
/Contents 2 0 R
|
||||
/Parent 5 0 R
|
||||
>>
|
||||
endobj
|
||||
|
||||
5 0 obj
|
||||
<< /Kids [ 4 0 R ]
|
||||
/Count 1
|
||||
/Type /Pages
|
||||
>>
|
||||
endobj
|
||||
|
||||
6 0 obj
|
||||
<< /Pages 5 0 R
|
||||
/Type /Catalog
|
||||
>>
|
||||
endobj
|
||||
|
||||
xref
|
||||
0 7
|
||||
0000000000 65535 f
|
||||
0000000010 00000 n
|
||||
0000000034 00000 n
|
||||
0000001245 00000 n
|
||||
0000001268 00000 n
|
||||
0000001441 00000 n
|
||||
0000001515 00000 n
|
||||
trailer
|
||||
<< /ID [ (some) (id) ]
|
||||
/Root 6 0 R
|
||||
/Size 7
|
||||
>>
|
||||
startxref
|
||||
1574
|
||||
%%EOF
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "emoji.fill.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
},
|
||||
"properties" : {
|
||||
"preserves-vector-representation" : true
|
||||
}
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
%PDF-1.7
|
||||
|
||||
1 0 obj
|
||||
<< >>
|
||||
endobj
|
||||
|
||||
2 0 obj
|
||||
<< /Length 3 0 R >>
|
||||
stream
|
||||
/DeviceRGB CS
|
||||
/DeviceRGB cs
|
||||
q
|
||||
1.000000 0.000000 -0.000000 1.000000 1.998444 1.997925 cm
|
||||
0.000000 0.000000 0.000000 scn
|
||||
10.001551 20.003113 m
|
||||
15.525254 20.003113 20.003101 15.525267 20.003101 10.001562 c
|
||||
20.003101 4.477859 15.525254 0.000013 10.001551 0.000013 c
|
||||
4.477847 0.000013 0.000000 4.477859 0.000000 10.001562 c
|
||||
0.000000 15.525267 4.477847 20.003113 10.001551 20.003113 c
|
||||
h
|
||||
6.463291 7.218245 m
|
||||
6.206941 7.543602 5.735374 7.599545 5.410017 7.343195 c
|
||||
5.084659 7.086845 5.028717 6.615278 5.285066 6.289920 c
|
||||
6.415668 4.854965 8.138899 3.999996 10.001535 3.999996 c
|
||||
11.861678 3.999996 13.582860 4.852667 14.713623 6.284365 c
|
||||
14.970354 6.609422 14.914966 7.081055 14.589909 7.337786 c
|
||||
14.264852 7.594518 13.793221 7.539129 13.536489 7.214072 c
|
||||
12.687231 6.138799 11.397759 5.499995 10.001535 5.499995 c
|
||||
8.603443 5.499995 7.312427 6.140525 6.463291 7.218245 c
|
||||
h
|
||||
7.001998 13.250921 m
|
||||
6.312035 13.250921 5.752709 12.691595 5.752709 12.001632 c
|
||||
5.752709 11.311668 6.312035 10.752343 7.001998 10.752343 c
|
||||
7.691962 10.752343 8.251287 11.311668 8.251287 12.001632 c
|
||||
8.251287 12.691595 7.691962 13.250921 7.001998 13.250921 c
|
||||
h
|
||||
13.001999 13.250921 m
|
||||
12.312036 13.250921 11.752709 12.691595 11.752709 12.001632 c
|
||||
11.752709 11.311668 12.312036 10.752343 13.001999 10.752343 c
|
||||
13.691962 10.752343 14.251287 11.311668 14.251287 12.001632 c
|
||||
14.251287 12.691595 13.691962 13.250921 13.001999 13.250921 c
|
||||
h
|
||||
f
|
||||
n
|
||||
Q
|
||||
|
||||
endstream
|
||||
endobj
|
||||
|
||||
3 0 obj
|
||||
1401
|
||||
endobj
|
||||
|
||||
4 0 obj
|
||||
<< /Annots []
|
||||
/Type /Page
|
||||
/MediaBox [ 0.000000 0.000000 24.000000 24.000000 ]
|
||||
/Resources 1 0 R
|
||||
/Contents 2 0 R
|
||||
/Parent 5 0 R
|
||||
>>
|
||||
endobj
|
||||
|
||||
5 0 obj
|
||||
<< /Kids [ 4 0 R ]
|
||||
/Count 1
|
||||
/Type /Pages
|
||||
>>
|
||||
endobj
|
||||
|
||||
6 0 obj
|
||||
<< /Pages 5 0 R
|
||||
/Type /Catalog
|
||||
>>
|
||||
endobj
|
||||
|
||||
xref
|
||||
0 7
|
||||
0000000000 65535 f
|
||||
0000000010 00000 n
|
||||
0000000034 00000 n
|
||||
0000001491 00000 n
|
||||
0000001514 00000 n
|
||||
0000001687 00000 n
|
||||
0000001761 00000 n
|
||||
trailer
|
||||
<< /ID [ (some) (id) ]
|
||||
/Root 6 0 R
|
||||
/Size 7
|
||||
>>
|
||||
startxref
|
||||
1820
|
||||
%%EOF
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "People Add.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
},
|
||||
"properties" : {
|
||||
"preserves-vector-representation" : true
|
||||
}
|
||||
}
|
150
MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Scene/Compose/people.add.imageset/People Add.pdf
vendored
Normal file
150
MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Scene/Compose/people.add.imageset/People Add.pdf
vendored
Normal file
|
@ -0,0 +1,150 @@
|
|||
%PDF-1.7
|
||||
|
||||
1 0 obj
|
||||
<< >>
|
||||
endobj
|
||||
|
||||
2 0 obj
|
||||
<< /Length 3 0 R >>
|
||||
stream
|
||||
/DeviceRGB CS
|
||||
/DeviceRGB cs
|
||||
q
|
||||
1.000000 0.000000 -0.000000 1.000000 2.000000 1.000000 cm
|
||||
0.000000 0.000000 0.000000 scn
|
||||
15.500000 11.000000 m
|
||||
18.537567 11.000000 21.000000 8.537566 21.000000 5.500000 c
|
||||
21.000000 2.462433 18.537567 0.000000 15.500000 0.000000 c
|
||||
12.462434 0.000000 10.000000 2.462433 10.000000 5.500000 c
|
||||
10.000000 8.537566 12.462434 11.000000 15.500000 11.000000 c
|
||||
h
|
||||
2.000000 10.001000 m
|
||||
10.809396 9.999816 l
|
||||
10.383152 9.555614 10.019394 9.050992 9.732251 8.500078 c
|
||||
2.000000 8.501000 l
|
||||
1.899344 8.491000 l
|
||||
1.774960 8.465720 1.690000 8.398199 1.646000 8.355000 c
|
||||
1.602800 8.311000 1.535280 8.226681 1.510000 8.102040 c
|
||||
1.500000 8.001000 l
|
||||
1.500000 6.500000 l
|
||||
1.500000 5.491000 1.950000 4.778000 2.917000 4.257999 c
|
||||
3.743154 3.813076 4.919508 3.543680 6.182578 3.504868 c
|
||||
6.500000 3.500000 l
|
||||
6.817405 3.504868 l
|
||||
7.681085 3.531410 8.503904 3.665789 9.202351 3.890396 c
|
||||
9.326429 3.397497 9.508213 2.927378 9.739013 2.486986 c
|
||||
8.688712 2.137030 7.530568 2.000000 6.500000 2.000000 c
|
||||
3.777867 2.000000 0.164695 2.956049 0.005454 6.270353 c
|
||||
0.000000 6.500000 l
|
||||
0.000000 8.001000 l
|
||||
0.000000 9.105000 0.896000 10.001000 2.000000 10.001000 c
|
||||
h
|
||||
15.500000 8.998419 m
|
||||
15.410124 8.990363 l
|
||||
15.206031 8.953320 15.045100 8.792387 15.008057 8.588294 c
|
||||
15.000000 8.498419 l
|
||||
15.000000 6.000420 l
|
||||
12.500000 6.000000 l
|
||||
12.410125 5.991943 l
|
||||
12.206032 5.954900 12.045099 5.793969 12.008056 5.589876 c
|
||||
12.000000 5.500000 l
|
||||
12.008056 5.410124 l
|
||||
12.045099 5.206031 12.206032 5.045100 12.410125 5.008057 c
|
||||
12.500000 5.000000 l
|
||||
15.000000 5.000420 l
|
||||
15.000000 2.500000 l
|
||||
15.008057 2.410124 l
|
||||
15.045100 2.206030 15.206031 2.045101 15.410124 2.008057 c
|
||||
15.500000 2.000000 l
|
||||
15.589876 2.008057 l
|
||||
15.793969 2.045101 15.954900 2.206030 15.991943 2.410124 c
|
||||
16.000000 2.500000 l
|
||||
16.000000 5.000420 l
|
||||
18.500000 5.000000 l
|
||||
18.589876 5.008057 l
|
||||
18.793970 5.045100 18.954899 5.206031 18.991943 5.410124 c
|
||||
19.000000 5.500000 l
|
||||
18.991943 5.589876 l
|
||||
18.954899 5.793969 18.793970 5.954900 18.589876 5.991943 c
|
||||
18.500000 6.000000 l
|
||||
16.000000 6.000420 l
|
||||
16.000000 8.498419 l
|
||||
15.991943 8.588294 l
|
||||
15.954900 8.792387 15.793969 8.953320 15.589876 8.990363 c
|
||||
15.500000 8.998419 l
|
||||
h
|
||||
6.500000 21.000000 m
|
||||
8.985000 21.000000 11.000000 18.985001 11.000000 16.500000 c
|
||||
11.000000 14.015000 8.985000 12.000000 6.500000 12.000000 c
|
||||
4.015000 12.000000 2.000000 14.015000 2.000000 16.500000 c
|
||||
2.000000 18.985001 4.015000 21.000000 6.500000 21.000000 c
|
||||
h
|
||||
15.500000 19.000000 m
|
||||
17.433001 19.000000 19.000000 17.433001 19.000000 15.500000 c
|
||||
19.000000 13.566999 17.433001 12.000000 15.500000 12.000000 c
|
||||
13.567000 12.000000 12.000000 13.566999 12.000000 15.500000 c
|
||||
12.000000 17.433001 13.567000 19.000000 15.500000 19.000000 c
|
||||
h
|
||||
6.500000 19.500000 m
|
||||
4.846000 19.500000 3.500000 18.153999 3.500000 16.500000 c
|
||||
3.500000 14.846000 4.846000 13.500000 6.500000 13.500000 c
|
||||
8.154000 13.500000 9.500000 14.846000 9.500000 16.500000 c
|
||||
9.500000 18.153999 8.154000 19.500000 6.500000 19.500000 c
|
||||
h
|
||||
15.500000 17.500000 m
|
||||
14.397000 17.500000 13.500000 16.603001 13.500000 15.500000 c
|
||||
13.500000 14.396999 14.397000 13.500000 15.500000 13.500000 c
|
||||
16.603001 13.500000 17.500000 14.396999 17.500000 15.500000 c
|
||||
17.500000 16.603001 16.603001 17.500000 15.500000 17.500000 c
|
||||
h
|
||||
f
|
||||
n
|
||||
Q
|
||||
|
||||
endstream
|
||||
endobj
|
||||
|
||||
3 0 obj
|
||||
3220
|
||||
endobj
|
||||
|
||||
4 0 obj
|
||||
<< /Annots []
|
||||
/Type /Page
|
||||
/MediaBox [ 0.000000 0.000000 24.000000 24.000000 ]
|
||||
/Resources 1 0 R
|
||||
/Contents 2 0 R
|
||||
/Parent 5 0 R
|
||||
>>
|
||||
endobj
|
||||
|
||||
5 0 obj
|
||||
<< /Kids [ 4 0 R ]
|
||||
/Count 1
|
||||
/Type /Pages
|
||||
>>
|
||||
endobj
|
||||
|
||||
6 0 obj
|
||||
<< /Pages 5 0 R
|
||||
/Type /Catalog
|
||||
>>
|
||||
endobj
|
||||
|
||||
xref
|
||||
0 7
|
||||
0000000000 65535 f
|
||||
0000000010 00000 n
|
||||
0000000034 00000 n
|
||||
0000003310 00000 n
|
||||
0000003333 00000 n
|
||||
0000003506 00000 n
|
||||
0000003580 00000 n
|
||||
trailer
|
||||
<< /ID [ (some) (id) ]
|
||||
/Root 6 0 R
|
||||
/Size 7
|
||||
>>
|
||||
startxref
|
||||
3639
|
||||
%%EOF
|
15
MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Scene/Compose/poll.fill.imageset/Contents.json
vendored
Normal file
15
MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Scene/Compose/poll.fill.imageset/Contents.json
vendored
Normal file
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "poll.fill.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
},
|
||||
"properties" : {
|
||||
"preserves-vector-representation" : true
|
||||
}
|
||||
}
|
89
MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Scene/Compose/poll.fill.imageset/poll.fill.pdf
vendored
Normal file
89
MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Scene/Compose/poll.fill.imageset/poll.fill.pdf
vendored
Normal file
|
@ -0,0 +1,89 @@
|
|||
%PDF-1.7
|
||||
|
||||
1 0 obj
|
||||
<< >>
|
||||
endobj
|
||||
|
||||
2 0 obj
|
||||
<< /Length 3 0 R >>
|
||||
stream
|
||||
/DeviceRGB CS
|
||||
/DeviceRGB cs
|
||||
q
|
||||
1.000000 0.000000 -0.000000 1.000000 2.000000 1.998138 cm
|
||||
0.000000 0.000000 0.000000 scn
|
||||
9.751870 20.002289 m
|
||||
11.271687 20.002289 12.503741 18.770235 12.503741 17.250418 c
|
||||
12.503741 2.751883 l
|
||||
12.503741 1.232067 11.271687 0.000013 9.751870 0.000013 c
|
||||
8.232054 0.000013 7.000000 1.232067 7.000000 2.751883 c
|
||||
7.000000 17.250418 l
|
||||
7.000000 18.770235 8.232054 20.002289 9.751870 20.002289 c
|
||||
h
|
||||
16.751871 15.002289 m
|
||||
18.271687 15.002289 19.503740 13.770235 19.503740 12.250419 c
|
||||
19.503740 2.751883 l
|
||||
19.503740 1.232067 18.271687 0.000013 16.751871 0.000013 c
|
||||
15.232055 0.000013 14.000000 1.232067 14.000000 2.751883 c
|
||||
14.000000 12.250419 l
|
||||
14.000000 13.770235 15.232055 15.002289 16.751871 15.002289 c
|
||||
h
|
||||
2.751871 10.002289 m
|
||||
4.271687 10.002289 5.503741 8.770235 5.503741 7.250419 c
|
||||
5.503741 2.751883 l
|
||||
5.503741 1.232067 4.271687 0.000013 2.751871 0.000013 c
|
||||
1.232054 0.000013 0.000000 1.232067 0.000000 2.751883 c
|
||||
0.000000 7.250419 l
|
||||
0.000000 8.770235 1.232054 10.002289 2.751871 10.002289 c
|
||||
h
|
||||
f
|
||||
n
|
||||
Q
|
||||
|
||||
endstream
|
||||
endobj
|
||||
|
||||
3 0 obj
|
||||
1024
|
||||
endobj
|
||||
|
||||
4 0 obj
|
||||
<< /Annots []
|
||||
/Type /Page
|
||||
/MediaBox [ 0.000000 0.000000 24.000000 24.000000 ]
|
||||
/Resources 1 0 R
|
||||
/Contents 2 0 R
|
||||
/Parent 5 0 R
|
||||
>>
|
||||
endobj
|
||||
|
||||
5 0 obj
|
||||
<< /Kids [ 4 0 R ]
|
||||
/Count 1
|
||||
/Type /Pages
|
||||
>>
|
||||
endobj
|
||||
|
||||
6 0 obj
|
||||
<< /Pages 5 0 R
|
||||
/Type /Catalog
|
||||
>>
|
||||
endobj
|
||||
|
||||
xref
|
||||
0 7
|
||||
0000000000 65535 f
|
||||
0000000010 00000 n
|
||||
0000000034 00000 n
|
||||
0000001114 00000 n
|
||||
0000001137 00000 n
|
||||
0000001310 00000 n
|
||||
0000001384 00000 n
|
||||
trailer
|
||||
<< /ID [ (some) (id) ]
|
||||
/Root 6 0 R
|
||||
/Size 7
|
||||
>>
|
||||
startxref
|
||||
1443
|
||||
%%EOF
|
|
@ -131,10 +131,17 @@ public enum Asset {
|
|||
public enum Scene {
|
||||
public enum Compose {
|
||||
public static let buttonTint = ColorAsset(name: "Scene/Compose/button.tint")
|
||||
public static let chatWarningFill = ImageAsset(name: "Scene/Compose/chat.warning.fill")
|
||||
public static let chatWarning = ImageAsset(name: "Scene/Compose/chat.warning")
|
||||
public static let earth = ImageAsset(name: "Scene/Compose/earth")
|
||||
public static let emojiFill = ImageAsset(name: "Scene/Compose/emoji.fill")
|
||||
public static let emoji = ImageAsset(name: "Scene/Compose/emoji")
|
||||
public static let media = ImageAsset(name: "Scene/Compose/media")
|
||||
public static let mention = ImageAsset(name: "Scene/Compose/mention")
|
||||
public static let more = ImageAsset(name: "Scene/Compose/more")
|
||||
public static let peopleAdd = ImageAsset(name: "Scene/Compose/people.add")
|
||||
public static let people = ImageAsset(name: "Scene/Compose/people")
|
||||
public static let pollFill = ImageAsset(name: "Scene/Compose/poll.fill")
|
||||
public static let poll = ImageAsset(name: "Scene/Compose/poll")
|
||||
}
|
||||
public enum Discovery {
|
||||
|
|
|
@ -0,0 +1,104 @@
|
|||
//
|
||||
// PollComposeItem.swift
|
||||
//
|
||||
//
|
||||
// Created by MainasuK on 2021-11-29.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import Combine
|
||||
import MastodonLocalization
|
||||
|
||||
public enum PollComposeItem: Hashable {
|
||||
case option(Option)
|
||||
case expireConfiguration(ExpireConfiguration)
|
||||
case multipleConfiguration(MultipleConfiguration)
|
||||
}
|
||||
|
||||
extension PollComposeItem {
|
||||
public final class Option: NSObject, Identifiable, ObservableObject {
|
||||
public let id = UUID()
|
||||
|
||||
public weak var textField: UITextField?
|
||||
|
||||
@Published public var text = ""
|
||||
@Published public var shouldBecomeFirstResponder = false
|
||||
|
||||
public override init() {
|
||||
super.init()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension PollComposeItem {
|
||||
public final class ExpireConfiguration: Identifiable, Hashable, ObservableObject {
|
||||
public let id = UUID()
|
||||
|
||||
@Published public var option: Option = .oneDay // Mastodon
|
||||
|
||||
public init() {
|
||||
// end init
|
||||
}
|
||||
|
||||
public static func == (lhs: ExpireConfiguration, rhs: ExpireConfiguration) -> Bool {
|
||||
return lhs.id == rhs.id
|
||||
&& lhs.option == rhs.option
|
||||
}
|
||||
|
||||
public func hash(into hasher: inout Hasher) {
|
||||
hasher.combine(id)
|
||||
}
|
||||
|
||||
public enum Option: String, Hashable, CaseIterable {
|
||||
case thirtyMinutes
|
||||
case oneHour
|
||||
case sixHours
|
||||
case oneDay
|
||||
case threeDays
|
||||
case sevenDays
|
||||
|
||||
public 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
|
||||
}
|
||||
}
|
||||
|
||||
public 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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension PollComposeItem {
|
||||
public final class MultipleConfiguration: Hashable, ObservableObject {
|
||||
private let id = UUID()
|
||||
|
||||
@Published public var isMultiple = false
|
||||
|
||||
public init() {
|
||||
// end init
|
||||
}
|
||||
|
||||
public static func == (lhs: MultipleConfiguration, rhs: MultipleConfiguration) -> Bool {
|
||||
return lhs.id == rhs.id
|
||||
&& lhs.isMultiple == rhs.isMultiple
|
||||
}
|
||||
|
||||
public func hash(into hasher: inout Hasher) {
|
||||
hasher.combine(id)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
//
|
||||
// PollComposeSection.swift
|
||||
//
|
||||
//
|
||||
// Created by MainasuK on 2021-11-29.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public enum PollComposeSection: Hashable {
|
||||
case main
|
||||
}
|
|
@ -9,6 +9,7 @@ import os.log
|
|||
import UIKit
|
||||
import SwiftUI
|
||||
import Combine
|
||||
import PhotosUI
|
||||
import MastodonCore
|
||||
|
||||
public final class ComposeContentViewController: UIViewController {
|
||||
|
@ -17,7 +18,7 @@ public final class ComposeContentViewController: UIViewController {
|
|||
|
||||
var disposeBag = Set<AnyCancellable>()
|
||||
public var viewModel: ComposeContentViewModel!
|
||||
let composeContentToolbarViewModel = ComposeContentToolbarView.ViewModel()
|
||||
private(set) lazy var composeContentToolbarViewModel = ComposeContentToolbarView.ViewModel(delegate: self)
|
||||
|
||||
let tableView: ComposeTableView = {
|
||||
let tableView = ComposeTableView()
|
||||
|
@ -31,6 +32,34 @@ public final class ComposeContentViewController: UIViewController {
|
|||
lazy var composeContentToolbarView = ComposeContentToolbarView(viewModel: composeContentToolbarViewModel)
|
||||
var composeContentToolbarViewBottomLayoutConstraint: NSLayoutConstraint!
|
||||
let composeContentToolbarBackgroundView = UIView()
|
||||
|
||||
// media picker
|
||||
|
||||
static func createPhotoLibraryPickerConfiguration(selectionLimit: Int = 4) -> PHPickerConfiguration {
|
||||
var configuration = PHPickerConfiguration()
|
||||
configuration.filter = .any(of: [.images, .videos])
|
||||
configuration.selectionLimit = selectionLimit
|
||||
return configuration
|
||||
}
|
||||
|
||||
private(set) lazy var photoLibraryPicker: PHPickerViewController = {
|
||||
let imagePicker = PHPickerViewController(configuration: ComposeContentViewController.createPhotoLibraryPickerConfiguration())
|
||||
imagePicker.delegate = self
|
||||
return imagePicker
|
||||
}()
|
||||
|
||||
private(set) lazy var imagePickerController: UIImagePickerController = {
|
||||
let imagePickerController = UIImagePickerController()
|
||||
imagePickerController.sourceType = .camera
|
||||
imagePickerController.delegate = self
|
||||
return imagePickerController
|
||||
}()
|
||||
|
||||
private(set) lazy var documentPickerController: UIDocumentPickerViewController = {
|
||||
let documentPickerController = UIDocumentPickerViewController(forOpeningContentTypes: [.image, .movie])
|
||||
documentPickerController.delegate = self
|
||||
return documentPickerController
|
||||
}()
|
||||
|
||||
deinit {
|
||||
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
|
||||
|
@ -188,6 +217,9 @@ extension ComposeContentViewController {
|
|||
}
|
||||
}
|
||||
.store(in: &disposeBag)
|
||||
|
||||
// bind toolbar
|
||||
bindToolbarViewModel()
|
||||
}
|
||||
|
||||
public override func viewDidLayoutSubviews() {
|
||||
|
@ -223,6 +255,12 @@ extension ComposeContentViewController {
|
|||
tableView.backgroundColor = backgroundColor
|
||||
composeContentToolbarBackgroundView.backgroundColor = theme.composeToolbarBackgroundColor
|
||||
}
|
||||
|
||||
private func bindToolbarViewModel() {
|
||||
viewModel.$isPollActive.assign(to: &composeContentToolbarViewModel.$isPollActive)
|
||||
viewModel.$isEmojiActive.assign(to: &composeContentToolbarViewModel.$isEmojiActive)
|
||||
viewModel.$isContentWarningActive.assign(to: &composeContentToolbarViewModel.$isContentWarningActive)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UIScrollViewDelegate
|
||||
|
@ -279,3 +317,101 @@ extension ComposeContentViewController {
|
|||
// MARK: - UITableViewDelegate
|
||||
extension ComposeContentViewController: UITableViewDelegate { }
|
||||
|
||||
// MARK: - PHPickerViewControllerDelegate
|
||||
extension ComposeContentViewController: PHPickerViewControllerDelegate {
|
||||
public func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
|
||||
picker.dismiss(animated: true, completion: nil)
|
||||
|
||||
// TODO:
|
||||
// let attachmentServices: [MastodonAttachmentService] = results.map { result in
|
||||
// let service = MastodonAttachmentService(
|
||||
// context: context,
|
||||
// pickerResult: result,
|
||||
// initialAuthenticationBox: viewModel.authenticationBox
|
||||
// )
|
||||
// return service
|
||||
// }
|
||||
// viewModel.attachmentServices = viewModel.attachmentServices + attachmentServices
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UIImagePickerControllerDelegate
|
||||
extension ComposeContentViewController: UIImagePickerControllerDelegate & UINavigationControllerDelegate {
|
||||
public func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
|
||||
picker.dismiss(animated: true, completion: nil)
|
||||
|
||||
guard let image = info[.originalImage] as? UIImage else { return }
|
||||
|
||||
// let attachmentService = MastodonAttachmentService(
|
||||
// context: context,
|
||||
// image: image,
|
||||
// initialAuthenticationBox: viewModel.authenticationBox
|
||||
// )
|
||||
// viewModel.attachmentServices = viewModel.attachmentServices + [attachmentService]
|
||||
}
|
||||
|
||||
public func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
|
||||
os_log("%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
|
||||
picker.dismiss(animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UIDocumentPickerDelegate
|
||||
extension ComposeContentViewController: UIDocumentPickerDelegate {
|
||||
public func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
|
||||
guard let url = urls.first else { return }
|
||||
|
||||
// let attachmentService = MastodonAttachmentService(
|
||||
// context: context,
|
||||
// documentURL: url,
|
||||
// initialAuthenticationBox: viewModel.authenticationBox
|
||||
// )
|
||||
// viewModel.attachmentServices = viewModel.attachmentServices + [attachmentService]
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - ComposeContentToolbarViewDelegate
|
||||
extension ComposeContentViewController: ComposeContentToolbarViewDelegate {
|
||||
func composeContentToolbarView(
|
||||
_ viewModel: ComposeContentToolbarView.ViewModel,
|
||||
toolbarItemDidPressed action: ComposeContentToolbarView.ViewModel.Action
|
||||
) {
|
||||
switch action {
|
||||
case .attachment:
|
||||
assertionFailure()
|
||||
case .poll:
|
||||
self.viewModel.isPollActive.toggle()
|
||||
case .emoji:
|
||||
self.viewModel.isEmojiActive.toggle()
|
||||
case .contentWarning:
|
||||
self.viewModel.isContentWarningActive.toggle()
|
||||
case .visibility:
|
||||
assertionFailure()
|
||||
}
|
||||
}
|
||||
|
||||
func composeContentToolbarView(
|
||||
_ viewModel: ComposeContentToolbarView.ViewModel,
|
||||
attachmentMenuDidPressed action: ComposeContentToolbarView.ViewModel.AttachmentAction
|
||||
) {
|
||||
switch action {
|
||||
case .photoLibrary:
|
||||
present(photoLibraryPicker, animated: true, completion: nil)
|
||||
case .camera:
|
||||
present(imagePickerController, animated: true, completion: nil)
|
||||
case .browse:
|
||||
#if SNAPSHOT
|
||||
guard let image = UIImage(named: "Athens") else { return }
|
||||
|
||||
let attachmentService = MastodonAttachmentService(
|
||||
context: context,
|
||||
image: image,
|
||||
initialAuthenticationBox: viewModel.authenticationBox
|
||||
)
|
||||
viewModel.attachmentServices = viewModel.attachmentServices + [attachmentService]
|
||||
#else
|
||||
present(documentPickerController, animated: true, completion: nil)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,6 +30,20 @@ public final class ComposeContentViewModel: NSObject, ObservableObject {
|
|||
@Published var viewLayoutFrame = ViewLayoutFrame()
|
||||
@Published var authContext: AuthContext
|
||||
|
||||
// poll
|
||||
@Published var isPollActive = false
|
||||
@Published public var pollOptions: [PollComposeItem.Option] = {
|
||||
// initial with 2 options
|
||||
var options: [PollComposeItem.Option] = []
|
||||
options.append(PollComposeItem.Option())
|
||||
options.append(PollComposeItem.Option())
|
||||
return options
|
||||
}()
|
||||
@Published public var maxPollOptionLimit = 4
|
||||
|
||||
@Published var isEmojiActive = false
|
||||
@Published var isContentWarningActive = false
|
||||
|
||||
// output
|
||||
|
||||
// content
|
||||
|
@ -94,3 +108,65 @@ extension ComposeContentViewModel {
|
|||
}
|
||||
}
|
||||
|
||||
extension ComposeContentViewModel {
|
||||
func createNewPollOptionIfCould() {
|
||||
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public)")
|
||||
|
||||
guard pollOptions.count < maxPollOptionLimit else { return }
|
||||
let option = PollComposeItem.Option()
|
||||
option.shouldBecomeFirstResponder = true
|
||||
pollOptions.append(option)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - DeleteBackwardResponseTextFieldRelayDelegate
|
||||
extension ComposeContentViewModel: DeleteBackwardResponseTextFieldRelayDelegate {
|
||||
|
||||
func deleteBackwardResponseTextFieldDidReturn(_ textField: DeleteBackwardResponseTextField) {
|
||||
let index = textField.tag
|
||||
if index + 1 == pollOptions.count {
|
||||
createNewPollOptionIfCould()
|
||||
} else if index < pollOptions.count {
|
||||
pollOptions[index + 1].textField?.becomeFirstResponder()
|
||||
}
|
||||
}
|
||||
|
||||
func deleteBackwardResponseTextField(_ textField: DeleteBackwardResponseTextField, textBeforeDelete: String?) {
|
||||
guard (textBeforeDelete ?? "").isEmpty else {
|
||||
// do nothing when not empty
|
||||
return
|
||||
}
|
||||
|
||||
let index = textField.tag
|
||||
guard index > 0 else {
|
||||
// do nothing at first row
|
||||
return
|
||||
}
|
||||
|
||||
func optionBeforeRemoved() -> PollComposeItem.Option? {
|
||||
guard index > 0 else { return nil }
|
||||
let indexBeforeRemoved = pollOptions.index(before: index)
|
||||
let itemBeforeRemoved = pollOptions[indexBeforeRemoved]
|
||||
return itemBeforeRemoved
|
||||
|
||||
}
|
||||
|
||||
func optionAfterRemoved() -> PollComposeItem.Option? {
|
||||
guard index < pollOptions.count - 1 else { return nil }
|
||||
let indexAfterRemoved = pollOptions.index(after: index)
|
||||
let itemAfterRemoved = pollOptions[indexAfterRemoved]
|
||||
return itemAfterRemoved
|
||||
}
|
||||
|
||||
// move first responder
|
||||
let _option = optionBeforeRemoved() ?? optionAfterRemoved()
|
||||
_option?.textField?.becomeFirstResponder()
|
||||
|
||||
guard pollOptions.count > 2 else {
|
||||
// remove item when more then 2 options
|
||||
return
|
||||
}
|
||||
pollOptions.remove(at: index)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
//
|
||||
// PollOptionRow.swift
|
||||
//
|
||||
//
|
||||
// Created by MainasuK on 2022-5-31.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import MastodonCore
|
||||
|
||||
public struct PollOptionRow: View {
|
||||
|
||||
@ObservedObject var viewModel: PollComposeItem.Option
|
||||
|
||||
let index: Int?
|
||||
let deleteBackwardResponseTextFieldRelayDelegate: DeleteBackwardResponseTextFieldRelayDelegate?
|
||||
let configurationHandler: (DeleteBackwardResponseTextField) -> Void
|
||||
|
||||
public var body: some View {
|
||||
PollOptionTextField(
|
||||
text: $viewModel.text,
|
||||
index: index ?? -1,
|
||||
delegate: deleteBackwardResponseTextFieldRelayDelegate
|
||||
) { textField in
|
||||
viewModel.textField = textField
|
||||
configurationHandler(textField)
|
||||
}
|
||||
.onReceive(viewModel.$shouldBecomeFirstResponder) { shouldBecomeFirstResponder in
|
||||
guard shouldBecomeFirstResponder else { return }
|
||||
viewModel.shouldBecomeFirstResponder = false
|
||||
viewModel.textField?.becomeFirstResponder()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
//
|
||||
// PollOptionTextField.swift
|
||||
//
|
||||
//
|
||||
// Created by MainasuK on 2022-5-27.
|
||||
//
|
||||
|
||||
import os.log
|
||||
import UIKit
|
||||
import SwiftUI
|
||||
import Combine
|
||||
import MastodonCore
|
||||
import MastodonLocalization
|
||||
|
||||
public struct PollOptionTextField: UIViewRepresentable {
|
||||
|
||||
let textField = DeleteBackwardResponseTextField()
|
||||
|
||||
@Binding var text: String
|
||||
|
||||
let index: Int
|
||||
let delegate: DeleteBackwardResponseTextFieldRelayDelegate?
|
||||
let configurationHandler: (DeleteBackwardResponseTextField) -> Void
|
||||
|
||||
public func makeUIView(context: Context) -> DeleteBackwardResponseTextField {
|
||||
textField.setContentHuggingPriority(.defaultHigh, for: .vertical)
|
||||
textField.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
|
||||
textField.textInset = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)
|
||||
textField.borderStyle = .roundedRect
|
||||
textField.returnKeyType = .next
|
||||
return textField
|
||||
}
|
||||
|
||||
public func updateUIView(_ textField: DeleteBackwardResponseTextField, context: Context) {
|
||||
textField.tag = index
|
||||
textField.text = text
|
||||
textField.placeholder = {
|
||||
if index >= 0 {
|
||||
return L10n.Scene.Compose.Poll.optionNumber(index)
|
||||
} else {
|
||||
assertionFailure()
|
||||
return ""
|
||||
}
|
||||
}()
|
||||
textField.delegate = context.coordinator
|
||||
textField.deleteBackwardDelegate = context.coordinator
|
||||
context.coordinator.delegate = delegate
|
||||
configurationHandler(textField)
|
||||
}
|
||||
|
||||
public func makeCoordinator() -> Coordinator {
|
||||
Coordinator(self)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protocol DeleteBackwardResponseTextFieldRelayDelegate: AnyObject {
|
||||
func deleteBackwardResponseTextFieldDidReturn(_ textField: DeleteBackwardResponseTextField)
|
||||
func deleteBackwardResponseTextField(_ textField: DeleteBackwardResponseTextField, textBeforeDelete: String?)
|
||||
}
|
||||
|
||||
extension PollOptionTextField {
|
||||
public class Coordinator: NSObject {
|
||||
let logger = Logger(subsystem: "DeleteBackwardResponseTextFieldRepresentable.Coordinator", category: "Coordinator")
|
||||
|
||||
var disposeBag = Set<AnyCancellable>()
|
||||
weak var delegate: DeleteBackwardResponseTextFieldRelayDelegate?
|
||||
|
||||
let view: PollOptionTextField
|
||||
|
||||
init(_ view: PollOptionTextField) {
|
||||
self.view = view
|
||||
super.init()
|
||||
|
||||
NotificationCenter.default.publisher(for: UITextField.textDidChangeNotification, object: view.textField)
|
||||
.sink { [weak self] _ in
|
||||
guard let self = self else { return }
|
||||
self.view.text = view.textField.text ?? ""
|
||||
}
|
||||
.store(in: &disposeBag)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UITextFieldDelegate
|
||||
extension PollOptionTextField.Coordinator: UITextFieldDelegate {
|
||||
|
||||
public func textFieldShouldReturn(_ textField: UITextField) -> Bool {
|
||||
guard let textField = textField as? DeleteBackwardResponseTextField else {
|
||||
return true
|
||||
}
|
||||
delegate?.deleteBackwardResponseTextFieldDidReturn(textField)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
extension PollOptionTextField.Coordinator: DeleteBackwardResponseTextFieldDelegate {
|
||||
public func deleteBackwardResponseTextField(_ textField: DeleteBackwardResponseTextField, textBeforeDelete: String?) {
|
||||
delegate?.deleteBackwardResponseTextField(textField, textBeforeDelete: textBeforeDelete)
|
||||
}
|
||||
}
|
|
@ -8,11 +8,14 @@
|
|||
import SwiftUI
|
||||
import MastodonCore
|
||||
import MastodonAsset
|
||||
import MastodonLocalization
|
||||
import MastodonSDK
|
||||
|
||||
extension ComposeContentToolbarView {
|
||||
class ViewModel: ObservableObject {
|
||||
|
||||
weak var delegate: ComposeContentToolbarViewDelegate?
|
||||
|
||||
// input
|
||||
@Published var backgroundColor = ThemeService.shared.currentTheme.value.composeToolbarBackgroundColor
|
||||
@Published var visibility: Mastodon.Entity.Status.Visibility = .public
|
||||
|
@ -20,9 +23,16 @@ extension ComposeContentToolbarView {
|
|||
return [.public, .private, .direct]
|
||||
}
|
||||
|
||||
@Published var isPollActive = false
|
||||
@Published var isEmojiActive = false
|
||||
@Published var isContentWarningActive = false
|
||||
|
||||
// output
|
||||
|
||||
init() {
|
||||
init(delegate: ComposeContentToolbarViewDelegate) {
|
||||
self.delegate = delegate
|
||||
// end init
|
||||
|
||||
ThemeService.shared.currentTheme
|
||||
.map { $0.composeToolbarBackgroundColor }
|
||||
.assign(to: &$backgroundColor)
|
||||
|
@ -39,19 +49,71 @@ extension ComposeContentToolbarView.ViewModel {
|
|||
case contentWarning
|
||||
case visibility
|
||||
|
||||
var image: UIImage {
|
||||
var activeImage: UIImage {
|
||||
switch self {
|
||||
case .attachment:
|
||||
return Asset.Scene.Compose.media.image
|
||||
return Asset.Scene.Compose.media.image.withRenderingMode(.alwaysTemplate)
|
||||
case .poll:
|
||||
return Asset.Scene.Compose.poll.image
|
||||
return Asset.Scene.Compose.pollFill.image.withRenderingMode(.alwaysTemplate)
|
||||
case .emoji:
|
||||
return Asset.Scene.Compose.emoji.image
|
||||
return Asset.Scene.Compose.emojiFill.image.withRenderingMode(.alwaysTemplate)
|
||||
case .contentWarning:
|
||||
return Asset.Scene.Compose.chatWarning.image
|
||||
return Asset.Scene.Compose.chatWarningFill.image.withRenderingMode(.alwaysTemplate)
|
||||
case .visibility:
|
||||
return Asset.Scene.Compose.earth.image
|
||||
return Asset.Scene.Compose.earth.image.withRenderingMode(.alwaysTemplate)
|
||||
}
|
||||
}
|
||||
|
||||
var inactiveImage: UIImage {
|
||||
switch self {
|
||||
case .attachment:
|
||||
return Asset.Scene.Compose.media.image.withRenderingMode(.alwaysTemplate)
|
||||
case .poll:
|
||||
return Asset.Scene.Compose.poll.image.withRenderingMode(.alwaysTemplate)
|
||||
case .emoji:
|
||||
return Asset.Scene.Compose.emoji.image.withRenderingMode(.alwaysTemplate)
|
||||
case .contentWarning:
|
||||
return Asset.Scene.Compose.chatWarning.image.withRenderingMode(.alwaysTemplate)
|
||||
case .visibility:
|
||||
return Asset.Scene.Compose.earth.image.withRenderingMode(.alwaysTemplate)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum AttachmentAction: CaseIterable {
|
||||
case photoLibrary
|
||||
case camera
|
||||
case browse
|
||||
|
||||
var title: String {
|
||||
switch self {
|
||||
case .photoLibrary: return L10n.Scene.Compose.MediaSelection.photoLibrary
|
||||
case .camera: return L10n.Scene.Compose.MediaSelection.camera
|
||||
case .browse: return L10n.Scene.Compose.MediaSelection.browse
|
||||
}
|
||||
}
|
||||
|
||||
var image: UIImage {
|
||||
switch self {
|
||||
case .photoLibrary: return UIImage(systemName: "photo.on.rectangle")!
|
||||
case .camera: return UIImage(systemName: "camera")!
|
||||
case .browse: return UIImage(systemName: "ellipsis")!
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension ComposeContentToolbarView.ViewModel {
|
||||
func image(for action: Action) -> UIImage {
|
||||
switch action {
|
||||
case .poll:
|
||||
return isPollActive ? action.activeImage : action.inactiveImage
|
||||
case .emoji:
|
||||
return isEmojiActive ? action.activeImage : action.inactiveImage
|
||||
case .contentWarning:
|
||||
return isContentWarningActive ? action.activeImage : action.inactiveImage
|
||||
default:
|
||||
return action.inactiveImage
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,11 +5,21 @@
|
|||
// Created by MainasuK on 22/10/18.
|
||||
//
|
||||
|
||||
import os.log
|
||||
import SwiftUI
|
||||
import MastodonAsset
|
||||
import MastodonLocalization
|
||||
import MastodonSDK
|
||||
|
||||
protocol ComposeContentToolbarViewDelegate: AnyObject {
|
||||
func composeContentToolbarView(_ viewModel: ComposeContentToolbarView.ViewModel, toolbarItemDidPressed action: ComposeContentToolbarView.ViewModel.Action)
|
||||
func composeContentToolbarView(_ viewModel: ComposeContentToolbarView.ViewModel, attachmentMenuDidPressed action: ComposeContentToolbarView.ViewModel.AttachmentAction)
|
||||
}
|
||||
|
||||
struct ComposeContentToolbarView: View {
|
||||
|
||||
let logger = Logger(subsystem: "ComposeContentToolbarView", category: "View")
|
||||
|
||||
static var toolbarHeight: CGFloat { 48 }
|
||||
|
||||
@ObservedObject var viewModel: ViewModel
|
||||
|
@ -20,20 +30,17 @@ struct ComposeContentToolbarView: View {
|
|||
switch action {
|
||||
case .attachment:
|
||||
Menu {
|
||||
Button {
|
||||
|
||||
} label: {
|
||||
Label("Photo Library", systemImage: "photo.on.rectangle")
|
||||
}
|
||||
Button {
|
||||
|
||||
} label: {
|
||||
Label("Camera", systemImage: "camera")
|
||||
}
|
||||
Button {
|
||||
|
||||
} label: {
|
||||
Label("Browse", systemImage: "ellipsis")
|
||||
ForEach(ComposeContentToolbarView.ViewModel.AttachmentAction.allCases, id: \.self) { attachmentAction in
|
||||
Button {
|
||||
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public), \(attachmentAction.title)")
|
||||
viewModel.delegate?.composeContentToolbarView(viewModel, attachmentMenuDidPressed: attachmentAction)
|
||||
} label: {
|
||||
Label {
|
||||
Text(attachmentAction.title)
|
||||
} icon: {
|
||||
Image(uiImage: attachmentAction.image)
|
||||
}
|
||||
}
|
||||
}
|
||||
} label: {
|
||||
label(for: action)
|
||||
|
@ -43,18 +50,23 @@ struct ComposeContentToolbarView: View {
|
|||
Menu {
|
||||
Picker(selection: $viewModel.visibility) {
|
||||
ForEach(viewModel.allVisibilities, id: \.self) { visibility in
|
||||
Label(visibility.rawValue, systemImage: "photo.on.rectangle")
|
||||
Label {
|
||||
Text(visibility.title)
|
||||
} icon: {
|
||||
Image(uiImage: visibility.image)
|
||||
}
|
||||
}
|
||||
} label: {
|
||||
Text("Select Visibility")
|
||||
Text(viewModel.visibility.title)
|
||||
}
|
||||
} label: {
|
||||
label(for: action)
|
||||
label(for: viewModel.visibility.image)
|
||||
}
|
||||
.frame(width: 48, height: 48)
|
||||
default:
|
||||
Button {
|
||||
|
||||
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): \(String(describing: action))")
|
||||
viewModel.delegate?.composeContentToolbarView(viewModel, toolbarItemDidPressed: action)
|
||||
} label: {
|
||||
label(for: action)
|
||||
}
|
||||
|
@ -72,11 +84,38 @@ struct ComposeContentToolbarView: View {
|
|||
|
||||
}
|
||||
|
||||
|
||||
extension ComposeContentToolbarView {
|
||||
func label(for action: ComposeContentToolbarView.ViewModel.Action) -> some View {
|
||||
Image(uiImage: action.image.withRenderingMode(.alwaysTemplate))
|
||||
Image(uiImage: viewModel.image(for: action))
|
||||
.foregroundColor(Color(Asset.Scene.Compose.buttonTint.color))
|
||||
.frame(width: 24, height: 24, alignment: .center)
|
||||
}
|
||||
|
||||
func label(for image: UIImage) -> some View {
|
||||
Image(uiImage: image)
|
||||
.foregroundColor(Color(Asset.Scene.Compose.buttonTint.color))
|
||||
.frame(width: 24, height: 24, alignment: .center)
|
||||
}
|
||||
}
|
||||
|
||||
extension Mastodon.Entity.Status.Visibility {
|
||||
fileprivate var title: String {
|
||||
switch self {
|
||||
case .public: return L10n.Scene.Compose.Visibility.public
|
||||
case .unlisted: return L10n.Scene.Compose.Visibility.unlisted
|
||||
case .private: return L10n.Scene.Compose.Visibility.private
|
||||
case .direct: return L10n.Scene.Compose.Visibility.direct
|
||||
case ._other(let value): return value
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate var image: UIImage {
|
||||
switch self {
|
||||
case .public: return Asset.Scene.Compose.earth.image.withRenderingMode(.alwaysTemplate)
|
||||
case .unlisted: return Asset.Scene.Compose.people.image.withRenderingMode(.alwaysTemplate)
|
||||
case .private: return Asset.Scene.Compose.peopleAdd.image.withRenderingMode(.alwaysTemplate)
|
||||
case .direct: return Asset.Scene.Compose.mention.image.withRenderingMode(.alwaysTemplate)
|
||||
case ._other: return Asset.Scene.Compose.more.image.withRenderingMode(.alwaysTemplate)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
import os.log
|
||||
import SwiftUI
|
||||
import MastodonCore
|
||||
import MastodonLocalization
|
||||
|
||||
public struct ComposeContentView: View {
|
||||
|
@ -21,8 +22,10 @@ public struct ComposeContentView: View {
|
|||
public var body: some View {
|
||||
VStack(spacing: .zero) {
|
||||
Group {
|
||||
// author
|
||||
authorView
|
||||
.padding(.top, 14)
|
||||
// content editor
|
||||
MetaTextViewRepresentable(
|
||||
string: $viewModel.content,
|
||||
width: viewModel.viewLayoutFrame.layoutFrame.width - ComposeContentView.margin * 2,
|
||||
|
@ -44,15 +47,17 @@ public struct ComposeContentView: View {
|
|||
)
|
||||
.frame(minHeight: 100)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
// poll
|
||||
pollView
|
||||
}
|
||||
.background(
|
||||
GeometryReader { proxy in
|
||||
Color.clear.preference(key: ViewFramePreferenceKey.self, value: proxy.frame(in: .local))
|
||||
}
|
||||
.onPreferenceChange(ViewFramePreferenceKey.self) { frame in
|
||||
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): content frame: \(frame.debugDescription)")
|
||||
viewModel.contentCellFrame = frame
|
||||
}
|
||||
.onPreferenceChange(ViewFramePreferenceKey.self) { frame in
|
||||
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): content frame: \(frame.debugDescription)")
|
||||
viewModel.contentCellFrame = frame
|
||||
}
|
||||
)
|
||||
Spacer()
|
||||
} // end VStack
|
||||
|
@ -84,6 +89,81 @@ extension ComposeContentView {
|
|||
}
|
||||
}
|
||||
|
||||
extension ComposeContentView {
|
||||
// MARK: - poll
|
||||
var pollView: some View {
|
||||
VStack {
|
||||
if viewModel.isPollActive {
|
||||
// poll option TextField
|
||||
ReorderableForEach(
|
||||
items: $viewModel.pollOptions
|
||||
) { $pollOption in
|
||||
let _index = viewModel.pollOptions.firstIndex(of: pollOption)
|
||||
PollOptionRow(
|
||||
viewModel: pollOption,
|
||||
index: _index,
|
||||
deleteBackwardResponseTextFieldRelayDelegate: viewModel
|
||||
) { textField in
|
||||
// viewModel.customEmojiPickerInputViewModel.configure(textInput: textField)
|
||||
}
|
||||
}
|
||||
}
|
||||
VStack(spacing: .zero) {
|
||||
// expire configuration
|
||||
Menu {
|
||||
ForEach(PollComposeItem.ExpireConfiguration.Option.allCases, id: \.self) { option in
|
||||
Button {
|
||||
// viewModel.pollExpireConfiguration.option = option
|
||||
// viewModel.pollExpireConfiguration = viewModel.pollExpireConfiguration
|
||||
} label: {
|
||||
Text(option.title)
|
||||
}
|
||||
}
|
||||
} label: {
|
||||
HStack {
|
||||
// VectorImageView(
|
||||
// image: Asset.ObjectTools.clock.image.withRenderingMode(.alwaysTemplate),
|
||||
// tintColor: .secondaryLabel
|
||||
// )
|
||||
// .frame(width: 24, height: 24)
|
||||
// .padding(.vertical, 12)
|
||||
// let text = viewModel.pollExpireConfigurationFormatter.string(from: TimeInterval(viewModel.pollExpireConfiguration.option.seconds)) ?? "-"
|
||||
// Text(text)
|
||||
// .font(.callout)
|
||||
// .foregroundColor(.primary)
|
||||
// Spacer()
|
||||
// VectorImageView(
|
||||
// image: Asset.Arrows.tablerChevronDown.image.withRenderingMode(.alwaysTemplate),
|
||||
// tintColor: .secondaryLabel
|
||||
// )
|
||||
// .frame(width: 24, height: 24)
|
||||
// .padding(.vertical, 12)
|
||||
}
|
||||
}
|
||||
// multi-selection configuration
|
||||
// Button {
|
||||
// viewModel.pollMultipleConfiguration.isMultiple.toggle()
|
||||
// viewModel.pollMultipleConfiguration = viewModel.pollMultipleConfiguration
|
||||
// } label: {
|
||||
// HStack {
|
||||
// let selectionImage = viewModel.pollMultipleConfiguration.isMultiple ? Asset.Indices.checkmarkSquare.image.withRenderingMode(.alwaysTemplate) : Asset.Indices.square.image.withRenderingMode(.alwaysTemplate)
|
||||
// VectorImageView(
|
||||
// image: selectionImage,
|
||||
// tintColor: .secondaryLabel
|
||||
// )
|
||||
// .frame(width: 24, height: 24)
|
||||
// .padding(.vertical, 12)
|
||||
// Text(L10n.Scene.Compose.Vote.multiple)
|
||||
// .font(.callout)
|
||||
// .foregroundColor(.primary)
|
||||
// Spacer()
|
||||
// }
|
||||
// }
|
||||
}
|
||||
} // end VStack
|
||||
}
|
||||
}
|
||||
|
||||
//private struct ScrollOffsetPreferenceKey: PreferenceKey {
|
||||
// static var defaultValue: CGPoint = .zero
|
||||
//
|
||||
|
@ -95,3 +175,25 @@ private struct ViewFramePreferenceKey: PreferenceKey {
|
|||
|
||||
static func reduce(value: inout CGRect, nextValue: () -> CGRect) { }
|
||||
}
|
||||
|
||||
// MARK: - TypeIdentifiedItemProvider
|
||||
extension PollComposeItem.Option: TypeIdentifiedItemProvider {
|
||||
public static var typeIdentifier: String {
|
||||
return Bundle(for: PollComposeItem.Option.self).bundleIdentifier! + String(describing: type(of: PollComposeItem.Option.self))
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - NSItemProviderWriting
|
||||
extension PollComposeItem.Option: NSItemProviderWriting {
|
||||
public func loadData(
|
||||
withTypeIdentifier typeIdentifier: String,
|
||||
forItemProviderCompletionHandler completionHandler: @escaping (Data?, Error?) -> Void
|
||||
) -> Progress? {
|
||||
completionHandler(nil, nil)
|
||||
return nil
|
||||
}
|
||||
|
||||
public static var writableTypeIdentifiersForItemProvider: [String] {
|
||||
return [Self.typeIdentifier]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,108 @@
|
|||
//
|
||||
// ReorderableForEach.swift
|
||||
//
|
||||
//
|
||||
// Created by MainasuK on 2022-5-23.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import UniformTypeIdentifiers
|
||||
|
||||
// Ref
|
||||
// https://stackoverflow.com/a/68963988/3797903
|
||||
|
||||
struct ReorderableForEach<Content: View, Item: Identifiable & Equatable & NSItemProviderWriting & TypeIdentifiedItemProvider>: View {
|
||||
|
||||
@State var currentReorderItem: Item? = nil
|
||||
@State var isCurrentReorderItemOutside: Bool = false
|
||||
|
||||
@Binding var items: [Item]
|
||||
@ViewBuilder let content: (Binding<Item>) -> Content
|
||||
|
||||
var body: some View {
|
||||
ForEach($items) { $item in
|
||||
content($item)
|
||||
.zIndex(currentReorderItem == item ? 1 : 0)
|
||||
.onDrop(
|
||||
of: [Item.typeIdentifier],
|
||||
delegate: DropRelocateDelegate(
|
||||
item: item,
|
||||
items: $items,
|
||||
current: $currentReorderItem,
|
||||
isOutside: $isCurrentReorderItemOutside
|
||||
)
|
||||
)
|
||||
.onDrag {
|
||||
currentReorderItem = item
|
||||
isCurrentReorderItemOutside = false
|
||||
return NSItemProvider(object: item)
|
||||
}
|
||||
}
|
||||
.onDrop(
|
||||
of: [Item.typeIdentifier],
|
||||
delegate: DropOutsideDelegate(
|
||||
current: $currentReorderItem,
|
||||
isOutside: $isCurrentReorderItemOutside
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
struct DropRelocateDelegate<Item: Equatable>: DropDelegate {
|
||||
let item: Item
|
||||
@Binding var items: [Item]
|
||||
|
||||
@Binding var current: Item?
|
||||
@Binding var isOutside: Bool
|
||||
|
||||
func dropEntered(info: DropInfo) {
|
||||
guard item != current, let current = current else { return }
|
||||
guard let from = items.firstIndex(of: current), let to = items.firstIndex(of: item) else { return }
|
||||
|
||||
if items[to] != current {
|
||||
withAnimation {
|
||||
items.move(
|
||||
fromOffsets: IndexSet(integer: from),
|
||||
toOffset: to > from ? to + 1 : to
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func dropUpdated(info: DropInfo) -> DropProposal? {
|
||||
DropProposal(operation: .move)
|
||||
}
|
||||
|
||||
func performDrop(info: DropInfo) -> Bool {
|
||||
current = nil
|
||||
isOutside = false
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
struct DropOutsideDelegate<Item: Equatable>: DropDelegate {
|
||||
@Binding var current: Item?
|
||||
@Binding var isOutside: Bool
|
||||
|
||||
func dropEntered(info: DropInfo) {
|
||||
isOutside = false
|
||||
}
|
||||
|
||||
func dropExited(info: DropInfo) {
|
||||
isOutside = true
|
||||
}
|
||||
|
||||
func dropUpdated(info: DropInfo) -> DropProposal? {
|
||||
return DropProposal(operation: .cancel)
|
||||
}
|
||||
|
||||
func performDrop(info: DropInfo) -> Bool {
|
||||
current = nil
|
||||
isOutside = false
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
public protocol TypeIdentifiedItemProvider {
|
||||
static var typeIdentifier: String { get }
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
//
|
||||
// VectorImageView.swift
|
||||
//
|
||||
//
|
||||
// Created by MainasuK on 2022-4-29.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import SwiftUI
|
||||
|
||||
// workaround SwiftUI vector image scale problem
|
||||
// https://stackoverflow.com/a/61178828/3797903
|
||||
public struct VectorImageView: UIViewRepresentable {
|
||||
|
||||
public var image: UIImage
|
||||
public var contentMode: UIView.ContentMode = .scaleAspectFit
|
||||
public var tintColor: UIColor = .black
|
||||
|
||||
public init(
|
||||
image: UIImage,
|
||||
contentMode: UIView.ContentMode = .scaleAspectFit,
|
||||
tintColor: UIColor = .black
|
||||
) {
|
||||
self.image = image
|
||||
self.contentMode = contentMode
|
||||
self.tintColor = tintColor
|
||||
}
|
||||
|
||||
public func makeUIView(context: Context) -> UIImageView {
|
||||
let imageView = UIImageView()
|
||||
imageView.setContentCompressionResistancePriority(
|
||||
.fittingSizeLevel,
|
||||
for: .vertical
|
||||
)
|
||||
return imageView
|
||||
}
|
||||
|
||||
public func updateUIView(_ imageView: UIImageView, context: Context) {
|
||||
imageView.contentMode = contentMode
|
||||
imageView.tintColor = tintColor
|
||||
imageView.image = image
|
||||
}
|
||||
|
||||
}
|
|
@ -15,10 +15,20 @@ public final class DeleteBackwardResponseTextField: UITextField {
|
|||
|
||||
public weak var deleteBackwardDelegate: DeleteBackwardResponseTextFieldDelegate?
|
||||
|
||||
public var textInset: UIEdgeInsets = .zero
|
||||
|
||||
public override func deleteBackward() {
|
||||
let text = self.text
|
||||
super.deleteBackward()
|
||||
deleteBackwardDelegate?.deleteBackwardResponseTextField(self, textBeforeDelete: text)
|
||||
}
|
||||
|
||||
public override func textRect(forBounds bounds: CGRect) -> CGRect {
|
||||
return bounds.inset(by: textInset)
|
||||
}
|
||||
|
||||
public override func editingRect(forBounds bounds: CGRect) -> CGRect {
|
||||
return bounds.inset(by: textInset)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue