-
Notifications
You must be signed in to change notification settings - Fork 149
Issue/8045 aztec image links #786
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
Issue/8045 aztec image links #786
Conversation
…ibuted string. Fixed issue with converting a link to HTML.
|
@jim-rhoades - Can you provide some sample testing steps? You can check out some of our other PRs for examples. |
|
Testing steps added! |
diegoreymendez
left a comment
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.
@jim-rhoades - I started with a logic and style check and added a few comments.
Let me know if there's anything.
| let childContent = serialize(child, inheriting: childAttributes) | ||
| content.append(childContent) | ||
| // if the element is of type '.a' and contains a '.img' | ||
| if element.isNodeType(.a) && element.children.count == 1 { |
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.
Readability / Maintainability:
From this point onward we have 2 if conditions that could be reduced to a single method call for clarity. Something along the lines of:
if isLinkedImage(element) {
// calculate content here
}| if let imgElement = element.children.first as? ElementNode, imgElement.isNodeType(.img) { | ||
|
|
||
| // get the link URL and assign it to the image attachment | ||
| if let linkText = element.stringValueForAttribute(named: "href") { |
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.
Logic: If this is false, we're losing content.
We should actually set linkText to "" when href is missing.
| // get the link URL and assign it to the image attachment | ||
| if let linkText = element.stringValueForAttribute(named: "href") { | ||
| let imgAttributes = self.attributes(for: imgElement, inheriting: attributes) | ||
| if let attachment = imgAttributes[NSAttachmentAttributeName] as? ImageAttachment, |
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.
Logic: There's a problem here since if this is false, we're losing content.
The truth is that the code we currently have doesn't support this scenario very well, so I'd recommend to deal with the other comments, and once that's done let's discuss this part of the code in Slack to try and provide a nice solution for it.
…dified code for clarity.
|
I've made some changes to address the first two issues you mentioned, which also fixed the problem with one of the tests not passing. |
diegoreymendez
left a comment
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.
Nice changes. I particularly like that you documented the new method.
I've added a few extra comments to try and improve both readability and maintainability.
Once we're done with these we chat to figure out the remaining logic.
| // if the node is of type '.a' with one child of type '.img', retrieve the '.img' node | ||
| if let imgElement = linkedImageElementFor(element) { | ||
| // get the link URL text | ||
| var linkText = "" |
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.
In order to avoid having linkText be a variable, you can reduce this and the next three lines into:
let linkText = element.stringValueForAttribute(named: "href") ?? ""| /// | ||
| /// - Returns: the child '.img' node if there is one | ||
| /// | ||
| fileprivate func linkedImageElementFor(_ element: ElementNode) -> ElementNode? { |
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.
This is really minor, but to align better with the rest of our code we'd normally declare this method as:
fileprivate func linkedImageElement(for element: ElementNode) -> ElementNode? {| return nil | ||
| } | ||
|
|
||
| guard let imgElement = element.children.first as? ElementNode, imgElement.isNodeType(.img) else { |
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.
You can group this up with the above guard, like so:
guard element.isNodeType(.a) && element.children.count == 1,
let imgElement = element.children.first as? ElementNode,
imgElement.isNodeType(.img) else {
return nil
}| content.append(attributedString) | ||
| } | ||
| } | ||
| // if the node is of type '.a' with one child of type '.img', retrieve the '.img' node |
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.
We should remove this method since it describes internal details of linkedImageElementFor() that are already documented in the method.
| } | ||
| // if the node is of type '.a' with one child of type '.img', retrieve the '.img' node | ||
| if let imgElement = linkedImageElementFor(element) { | ||
| // get the link URL text |
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.
We'd normally avoid explaining in comments what the code does, since the code itself is readable enough to describe it.
In cases where the code is not easy to read, it normally means some effort can be put towards making it more readable (like you did with the method linkedImageElementFor()).
diegoreymendez
left a comment
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.
Added some comments.
| element.updateAttribute(named: key, value: .string(finalValue)) | ||
| } | ||
|
|
||
| // if the image attachment has a linkURL set, |
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.
Remove the comments that explain what the code does, since that should be evident by just reading the code.
| if let imgElement = linkedImageElement(for: element) { | ||
| let linkText = element.stringValueForAttribute(named: "href") ?? "" | ||
| let imgAttributes = self.attributes(for: imgElement, inheriting: attributes) | ||
| if let attachment = imgAttributes[NSAttachmentAttributeName] as? ImageAttachment, |
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.
The specific problem we have at this point is that if this condition fails, we're losing content, since the element has no representation in the attributed string.
Upon inspecting the code in more depth I realized both of these conditions should succeed if we're dealing with an image element. If they ever failed it would mean we have broken logic somewhere.
The ideal solution would be to reengineer our formatters, but that'd be too big of a project for the scope of the trial.
In order to avoid a silent failure, we could use something like this:
let attachment = imgAttributes[NSAttachmentAttributeName] as! ImageAttachment
let attributedString = implicitRepresentation(for: imgElement, inheriting: imgAttributes)!PS: I know it can look counter-intuitive but it's basically a decision between silently ignoring an error situation vs letting it "explode" and become more noticeable (so we can fix it).
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.
Normally I would try to avoid force unwrapping optionals, but I can see how "letting it explode" would be better in this case - to make the failure noticeable. Will make that change momentarily.
diegoreymendez
left a comment
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.
Working as described.
We'll wait for the second review before merging.
frosty
left a comment
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.
Looking good! I ran through your testing steps and everything worked as expected. I've just left a couple of quite small code style comments. I'll mark the PR as approved, as I don't think they should hold it up, but one or two of them might be nice to quickly address before merging.
|
|
||
| if let linkText = attachment.linkURL?.absoluteString { | ||
| let hrefValue = Attribute.Value(withString: linkText) | ||
| let hrefAttribute = Attribute(name: "href", value: hrefValue) |
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.
I'm not very familiar with the Aztec codebase, but "href" here feels like something we could use a constant for, instead of using string literals.
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.
You're right, and I see there's already a HTMLLinkAttribute enum with a constant for that. I'll change it.
| } | ||
|
|
||
| if let imgElement = linkedImageElement(for: element) { | ||
| let linkText = element.stringValueForAttribute(named: "href") ?? "" |
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.
See earlier comment about "href"
|
|
||
| var attachment: ImageAttachment? | ||
| var onUpdate: ((ImageAttachment.Alignment, ImageAttachment.Size, URL, String?) -> Void)? | ||
| var onUpdate: ((ImageAttachment.Alignment, ImageAttachment.Size, URL, URL?, String?) -> Void)? |
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.
This closure is getting a little unwieldy, particularly for the urls and strings. I wonder if we should either add a comment that clarifies what each parameter does, or simply add names to the parameters:
var onUpdate: ((_ alignment: ImageAttachment.Alignment, _ size: ImageAttachment.Size, _ imageURL: URL, _ linkURL: URL?, _ altText: String?) -> Void)?
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.
The rest of the code seems to favor making things self-explanatory, rather than adding comments... so I think adding names to the parameters is a good idea. Making that change.
Unfortunately that doesn't make the point where you're using the closure any more readable since it's still:
onUpdate(alignment.toAttachmentAlignment(), size.toAttachmentSize(), url, linkURL, alt)
(because "Function types cannot have argument labels" ? meh)
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.
Right! But I guess at least in the sample you game, the variables you’re passing in all have explanatory names :)
| onUpdate(alignment.toAttachmentAlignment(), size.toAttachmentSize(), url, alt) | ||
| var linkURL: URL? | ||
| if let linkText = linkURLTextField.text { | ||
| linkURL = URL(string: linkText) |
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.
We could condense this like:
var linkURL = URL(string: linkURLTextField.text ?? "")
If you pass in an empty string, you get back nil.
diegoreymendez
left a comment
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.
Checked the latest changes and all is fine. Merging.
Nice work @jim-rhoades :)
This is the Aztec related work needed to implement adding links to images in posts from its detail editor.
One small detail to note is that in the example app, the
linkURLis automatically set to the same thing as the file URL after the image is added in EditorDemoController.Doing it that way, rather than automatically setting it within ImageAttachment, leaves it up to the developer integrating AztecEditor into their app to decide whether or not they want to automatically link images to themselves.
To test:
</>button to show that the link URL is shown within the HTML (with an<a href></a>containing the URL around the img tag)</>button again to show that the HTML is properly converted back to an NSAttributedString... and that the image displays properly