-
Notifications
You must be signed in to change notification settings - Fork 3
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
feat: Implement features from prose-core-views
0.4.0...0.9.0
#88
Conversation
9a59fc7
to
b2f3b61
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, Rémi!
A few notes…
At first glimpse introducing WebMessageHandler
seemed like a good idea to me. After thinking about this a little more deeply, I'm not so sure anymore.
I believe that introducing this new concept makes reasoning about the code harder. As an example, imagine you'd wanted to change something about the context menu without knowing the code base too much. Without these handlers you'd knew that there is no other way than that the context menu would be presented from the view - now this doesn't hold true anymore. Because it's not exactly clear what they are, these handlers are also prone to misuse. Are these views? Are they allowed to generate side-effects?
The alternative - for this feature set - would be that we'd add an extra messageReducer that receives the JS events as actions. This could be a forEach reducer. The messages should be held in an IdentifiedArray
, also because there's a lot of index searching going on already. In case encoding becomes a problem it could be done like that:
struct Obj: Encodable {
var items: [String] = ["a", "b", "c"]
func encode(to encoder: Encoder) throws {
var container = encoder.unkeyedContainer()
for item in self.items {
try container.encode(item)
}
}
}
try print(String(data: JSONEncoder().encode(Obj()), encoding: .utf8)!)
// prints ["a","b","c"]
The MessageMenu
could then be a plain struct (akin to AlertState) which is not tied to a specific platform. We'd then have a custom View
which interprets the MessageMenu
depending on the current platform.
Example for a modified JS event handler, building on your work…
struct ToggleReactionPayload: Codable {
var ids: [Message.ID]
var reaction: String
}
enum MessageAction {
case showReactions([Message.ID])
case toggleReaction(ToggleReactionPayload)
}
struct JSEventHandler {
var event: String
var actionFromBody: (Any) throws -> MessageAction
init<Payload: Decodable>(event: String, action: @escaping (Payload) -> MessageAction) {
self.event = event
self.actionFromBody = { body in
guard let bodyString: String = body as? String,
let bodyData: Data = bodyString.data(using: .utf8)
else {
logger.fault("JS message body should be serialized as a String")
// TODO: Throw exception
fatalError()
}
guard let payload = try? JSONDecoder().decode(Payload.self, from: bodyData) else {
logger.warning("JS message body could not be decoded as `Self.Body`. Content: \(bodyString)")
// TODO: Throw exception
fatalError()
}
return action(payload)
}
}
}
final class ViewStoreScriptMessageHandler: NSObject, WKScriptMessageHandler {
let viewStore: ViewStore<Void, MessageAction>
let handler: JSEventHandler
init(handler: JSEventHandler, viewStore: ViewStore<Void, MessageAction>) {
self.handler = handler
self.viewStore = viewStore
super.init()
}
func userContentController(
_ userContentController: WKUserContentController,
didReceive message: WKScriptMessage
) {
/// TODO: Handle exception
try! self.viewStore.send(self.handler.actionFromBody(message.body))
}
}
extension WKUserContentController {
func addEventHandler(_ handler: JSEventHandler, viewStore: ViewStore<Void, MessageAction>) {
let handlerName = "handler_" + handler.event.replacingOccurrences(of: ":", with: "_")
self.add(
ViewStoreScriptMessageHandler(handler: handler, viewStore: viewStore),
name: handlerName
)
let script = """
function \(handlerName)(content) {
// We need to send a parameter, or the call will not be forwarded to the `WKScriptMessageHandler`
window.webkit.messageHandlers.\(handlerName).postMessage(JSON.stringify(content));
}
MessagingEvent.on("\(handler.event)", \(handlerName));
"""
self.addUserScript(
WKUserScript(source: script, injectionTime: .atDocumentEnd, forMainFrameOnly: true)
)
}
}
// Usage
let handler = JSEventHandler(event: "message:reactions:view", action: MessageAction.showReactions)
What do you think? Am I missing anything?
Prose/ProseLib/Sources/ConversationFeature/Chat/Events/MessageMenu.swift
Outdated
Show resolved
Hide resolved
@nesium The problem with your approach is that you suppose all actions only need the message payload (serialized into the correct type). However, when the payload contains a point, we also need a reference to the |
@RemiBardon Given that the flow is WKScriptMessageHandler > ViewStore > Reducer > ViewStore > View and given that the view is the entity which presents other views and holds a reference to the WKWebView, why do we need to pass the WKWebView through all the way? |
Ok, I will try to present the picker from the view then. |
I think I fixed everything, except
and the few I'm not sure a Also, @nesium I need your opinion on this: How should I handle removing duplicates in
Do you have another idea? What would you suggest? |
faf4703
to
bf803f1
Compare
After a small talk with @nesium, I'm going for the second option, but using |
15d9699
to
7953a1b
Compare
As we changed the API in prose-core-views v10.0.1, I think we can delay the If I didn't miss anything, I fixed everything you asked for @nesium. Is everything good now? |
Oh, I forgot three |
@valeriansaliou What do you think? |
LGTM, can you also add a report button which would create a pre-filled Markdown-formatted email (for the error trace) that would send this to support+macos [at] (our domain) [dot] org ? That way it gets into Crisp and we can create a rule based on the |
I thought about it, but I didn't want to go super fancy in this PR (and I wanted to ask you how to do it properly). Do you mind if I open an issue for this, and we get on it when we introduce logs sharing? |
No description provided.