Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[iOS] Implement client-defined media options. #1539

Merged
merged 5 commits into from
Nov 7, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions ios/gutenberg/GutenbergViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,8 @@ extension GutenbergViewController: GutenbergBridgeDelegate {
print("↳ HTML: \(html)")
}

func gutenbergDidRequestMedia(from source: MediaPickerSource, filter: [MediaFilter]?, allowMultipleSelection: Bool, with callback: @escaping MediaPickerDidPickMediaCallback) {
guard let currentFilter = filter?.first else {
func gutenbergDidRequestMedia(from source: Gutenberg.MediaSource, filter: [Gutenberg.MediaType], allowMultipleSelection: Bool, with callback: @escaping MediaPickerDidPickMediaCallback) {
guard let currentFilter = filter.first else {
return
}
switch source {
Expand Down Expand Up @@ -95,6 +95,7 @@ extension GutenbergViewController: GutenbergBridgeDelegate {
case .deviceCamera:
print("Gutenberg did request a device media picker, opening the camera picker")
pickAndUpload(from: .camera, filter: currentFilter, callback: callback)
default: break
}
}

Expand All @@ -103,7 +104,7 @@ extension GutenbergViewController: GutenbergBridgeDelegate {
callback(MediaInfo(id: id, url: url.absoluteString, type: "image"))
}

func pickAndUpload(from source: UIImagePickerController.SourceType, filter: MediaFilter, callback: @escaping MediaPickerDidPickMediaCallback) {
func pickAndUpload(from source: UIImagePickerController.SourceType, filter: Gutenberg.MediaType, callback: @escaping MediaPickerDidPickMediaCallback) {
mediaPickCoordinator = MediaPickCoordinator(presenter: self, filter: filter, callback: { (url) in
guard let url = url, let mediaID = self.mediaUploadCoordinator.upload(url: url) else {
callback([MediaInfo(id: nil, url: nil, type: nil)])
Expand Down
4 changes: 2 additions & 2 deletions ios/gutenberg/MediaPickCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ class MediaPickCoordinator: NSObject, UIImagePickerControllerDelegate, UINavigat

private let presenter: UIViewController
private let callback: (URL?) -> Void
private let filter: MediaFilter
private let filter: Gutenberg.MediaType

init(presenter: UIViewController,
filter: MediaFilter,
filter: Gutenberg.MediaType,
callback: @escaping (URL?) -> Void) {
self.presenter = presenter
self.callback = callback
Expand Down
37 changes: 37 additions & 0 deletions react-native-gutenberg-bridge/ios/Gutenberg.swift
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ public class Gutenberg: NSObject {
self.dataSource = dataSource
self.extraModules = extraModules
super.init()
bridgeModule.dataSource = dataSource
logThreshold = isPackagerRunning ? .trace : .error
}

Expand Down Expand Up @@ -162,5 +163,41 @@ extension Gutenberg {
public enum MediaType: String {
case image
case video
case audio
case other
}
}

extension Gutenberg.MediaType {
init(fromJSString rawValue: String) {
self = Gutenberg.MediaType(rawValue: rawValue) ?? .other
}
}

extension Gutenberg {
public struct MediaSource: Hashable {
/// The label string that will be shown to the user.
let label: String

/// A unique identifier of this media source option.
let id: String

/// The types of media this source can provide.
let types: Set<MediaType>

var jsRepresentation: [String: String] {
return [
"label": label,
"value": id,
]
}
}
}

public extension Gutenberg.MediaSource {
public init(id: String, label: String, types: [Gutenberg.MediaType]) {
self.id = id
self.label = label
self.types = Set(types)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,13 @@ public protocol GutenbergBridgeDataSource: class {
///
/// - Returns: Gutenberg related localization key value pairs for the current locale.
func gutenbergTranslations() -> [String : [String]]?

/// Asks the delegate for a list of Media Sources to show on the Media Source Picker.
func gutenbergMediaSources() -> [Gutenberg.MediaSource]
}

public extension GutenbergBridgeDataSource {
func gutenbergMediaSources() -> [Gutenberg.MediaSource] {
return []
}
}
29 changes: 16 additions & 13 deletions react-native-gutenberg-bridge/ios/GutenbergBridgeDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,23 @@ public struct MediaInfo {
}

public typealias MediaPickerDidPickMediaCallback = (_ media: [MediaInfo]?) -> Void

public typealias MediaImportCallback = (_ media: MediaInfo?) -> Void

public enum MediaPickerSource: String {
case mediaLibrary = "SITE_MEDIA_LIBRARY"
case deviceLibrary = "DEVICE_MEDIA_LIBRARY"
case deviceCamera = "DEVICE_CAMERA"
}

public enum MediaFilter: String {
case image
case video
case audio
case other
Comment on lines -23 to -27
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Merged MediaType with MediaFilter since we filters by Types of media. It seemed like redundant.

/// Declare internal Media Sources.
/// Label and Type are not relevant since they are delcared on the JS side.
/// Hopefully soon, this will need to be declared on the client side.
extension Gutenberg.MediaSource {
public static let mediaLibrary = Gutenberg.MediaSource(id: "SITE_MEDIA_LIBRARY", label: "", types: [.image, .video])
public static let deviceLibrary = Gutenberg.MediaSource(id: "DEVICE_MEDIA_LIBRARY", label: "", types: [.image, .video])
public static let deviceCamera = Gutenberg.MediaSource(id: "DEVICE_CAMERA", label: "", types: [.image, .video])

static var registeredInternalSources: [Gutenberg.MediaSource] {
return [
.deviceCamera,
.deviceLibrary,
.mediaLibrary,
]
}
}

/// Ref. https://github.com/facebook/react-native/blob/master/Libraries/polyfills/console.js#L376
Expand Down Expand Up @@ -77,7 +80,7 @@ public protocol GutenbergBridgeDelegate: class {
/// - source: the source from where the picker will get the media
/// - callback: A callback block to be called with an array of upload mediaIdentifiers and a placeholder images file url, use nil on both parameters to signal that the action was canceled.
///
func gutenbergDidRequestMedia(from source: MediaPickerSource, filter: [MediaFilter]?, allowMultipleSelection: Bool, with callback: @escaping MediaPickerDidPickMediaCallback)
func gutenbergDidRequestMedia(from source: Gutenberg.MediaSource, filter: [Gutenberg.MediaType], allowMultipleSelection: Bool, with callback: @escaping MediaPickerDidPickMediaCallback)
Comment on lines -80 to +83
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Made filter non-optional, since a nil and empty both means no filters (use all types).


/// Tells the delegate that gutenberg JS requested the import of media item based on the provided URL
///
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
@objc (RNReactNativeGutenbergBridge)
public class RNReactNativeGutenbergBridge: RCTEventEmitter {
weak var delegate: GutenbergBridgeDelegate?
weak var dataSource: GutenbergBridgeDataSource?
private var isJSLoading = true
private var hasObservers = false

// MARK: - Messaging methods

@objc
Expand All @@ -14,13 +16,9 @@ public class RNReactNativeGutenbergBridge: RCTEventEmitter {

@objc
func requestMediaPickFrom(_ source: String, filter: [String]?, allowMultipleSelection: Bool, callback: @escaping RCTResponseSenderBlock) {
let mediaSource: MediaPickerSource = MediaPickerSource(rawValue: source) ?? .deviceLibrary
let mediaFilter: [MediaFilter]? = filter?.map({
if let type = MediaFilter(rawValue: $0) {
return type
}
return MediaFilter.other
})
let mediaSource = getMediaSource(withId: source)
let mediaFilter = getMediaTypes(from: filter)

DispatchQueue.main.async {
self.delegate?.gutenbergDidRequestMedia(from: mediaSource, filter: mediaFilter, allowMultipleSelection: allowMultipleSelection, with: { media in
guard let media = media else {
Expand All @@ -45,15 +43,25 @@ public class RNReactNativeGutenbergBridge: RCTEventEmitter {
})
}
}

@objc
func requestOtherMediaPickFrom(_ source: String, allowMultipleSelection: Bool, callback: @escaping RCTResponseSenderBlock) {
//TODO implement me
requestMediaPickFrom(source, filter: nil, allowMultipleSelection: allowMultipleSelection, callback: callback)
}
Comment on lines 48 to 50
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This event (requestOtherMediaPickFrom) is probably not needed? We could reuse requestMediaPickFrom.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, but better check with @marecar3 if this can be changed on Android side or is there any limitation there.

Copy link
Contributor Author

@etoledom etoledom Nov 5, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't mean to do it on this PR, but it's good to keep it in mind. There might also be a reason for it on Android?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Made this mentioned change here: WordPress/gutenberg#18303


@objc
func getOtherMediaOptions(_ filter: [String]?, callback: @escaping RCTResponseSenderBlock) {
//TODO implement me
guard let dataSource = dataSource else {
return callback([])
}

let mediaSources = dataSource.gutenbergMediaSources()
let allowedTypes = getMediaTypes(from: filter)
let filteredSources = mediaSources.filter {
return $0.types.intersection(allowedTypes).isEmpty == false
}
let jsMediaSources = filteredSources.map { $0.jsRepresentation }
callback([jsMediaSources])
}

@objc
Expand Down Expand Up @@ -194,13 +202,21 @@ extension RNReactNativeGutenbergBridge {
// MARK: - Helpers

extension RNReactNativeGutenbergBridge {

func optionalArray(from optionalString: String?) -> [String]? {
guard let string = optionalString else {
return nil
}
return [string]
}

private func getMediaSource(withId mediaSourceID: String) -> Gutenberg.MediaSource {
let allMediaSources = Gutenberg.MediaSource.registeredInternalSources + (dataSource?.gutenbergMediaSources() ?? [])
return allMediaSources.first{ $0.id == mediaSourceID } ?? .deviceLibrary
}

private func getMediaTypes(from jsMediaTypes: [String]?) -> [Gutenberg.MediaType] {
return (jsMediaTypes ?? []).map { Gutenberg.MediaType(fromJSString: $0) }
}
}

extension RNReactNativeGutenbergBridge {
Expand Down