Skip to content

Conversation

@jkmassel
Copy link
Contributor

@jkmassel jkmassel commented Nov 5, 2025

Description

Adds support for ticket attachment and status.

Testing instructions

Try:

  1. Creating a ticket – ensure the compose screen can't be dismissed without confirmation while you're writing a message.
  2. Try replying to a ticket – ensure the reply screen can't be dismissed without confirmation while you're writing a message.
  3. Creating a new support conversation. Ensure you can reply to it, and that the status is correct after you create it.
  4. Ensure you can't reply to a closed conversation.
  5. Ensure you can upload photo and video attachments.
  6. Ensure you can view previously-uploaded photo and video attachments.

Next Steps

A future PR will address localization, and will update wprs to address conversation titles being based on the last message, not the first.

IMG_5401 IMG_5400 IMG_5402

@dangermattic
Copy link
Collaborator

dangermattic commented Nov 5, 2025

3 Warnings
⚠️ Modules/Package.swift was changed without updating its corresponding Package.resolved.

If the change includes adding, removing, or editing a dependency please resolve the Swift packages as appropriate to your project setup (e.g. in Xcode or by running swift package resolve).

If the change to the Package.swift did not modify dependencies, ignoring this warning should be safe, but we recommend double checking and running the package resolution just in case.
.

⚠️ This PR is larger than 500 lines of changes. Please consider splitting it into smaller PRs for easier and faster reviews.
⚠️ This PR is assigned to the milestone 26.5. This milestone is due in less than 4 days.
Please make sure to get it merged by then or assign it to a milestone with a later deadline.

Generated by 🚫 Danger

@jkmassel jkmassel force-pushed the add/support-ticket-attachments branch 3 times, most recently from d014aae to f6aaec6 Compare November 6, 2025 01:59
@jkmassel jkmassel force-pushed the add/support-ticket-attachments branch from f6aaec6 to 9179bec Compare November 6, 2025 02:08
@wpmobilebot
Copy link
Contributor

wpmobilebot commented Nov 6, 2025

App Icon📲 You can test the changes from this Pull Request in WordPress by scanning the QR code below to install the corresponding build.
App NameWordPress
ConfigurationRelease-Alpha
Build Number29683
VersionPR #24972
Bundle IDorg.wordpress.alpha
Commit17b5120
Installation URL1365609bu70h8
Automatticians: You can use our internal self-serve MC tool to give yourself access to those builds if needed.

@wpmobilebot
Copy link
Contributor

wpmobilebot commented Nov 6, 2025

App Icon📲 You can test the changes from this Pull Request in Jetpack by scanning the QR code below to install the corresponding build.
App NameJetpack
ConfigurationRelease-Alpha
Build Number29683
VersionPR #24972
Bundle IDcom.jetpack.alpha
Commit17b5120
Installation URL5jq98to872ssg
Automatticians: You can use our internal self-serve MC tool to give yourself access to those builds if needed.

@jkmassel jkmassel self-assigned this Nov 6, 2025
@jkmassel jkmassel added this to the 26.5 milestone Nov 6, 2025
@jkmassel jkmassel marked this pull request as ready for review November 6, 2025 02:21
@adalpari
Copy link

adalpari commented Nov 6, 2025

Edited: I realised I cannot select videos to add them as attached files. Is that expected?

@adalpari
Copy link

adalpari commented Nov 6, 2025

Edited: I realised I cannot select videos to add them as attached files. Is that expected?

Screen.Recording.2025-11-06.at.16.14.10.mov

@adalpari
Copy link

adalpari commented Nov 6, 2025

Great! Now I can add screen-recordings, but when selecting one, I don't get any feedback about it being added (or not). It's a 10MB video.

ScreenRecording_11-06-2025.20-11-33_1.MP4

Copy link
Contributor

@kean kean left a comment

Choose a reason for hiding this comment

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

Hey, I left a few comments with things to potentially simplify. As a general feedback for the form, I'm going to put it this PR, but it's not directly related to attachments, so please feel to process these the way you see fit.

1. "Submit Request" form

I found the "Contact Support" a bit overwhelming and unapproachable. I wanted to see "Message", and it's not on the screen when you open it.

Screenshot 2025-11-06 at 3 21 27 PM

A couple of more specific items:

  • Should use a standard Button.make(role: .cancel) (x mark) and in the leading edge instead of "Cancel" in the confirmation button placement.
  • The "I need help with" looks like it supports multiple selection but doesn't. It takes a lot of vertical space.
  • Is "I need help with" required and/or important? Is that something HEs care about? If not, I'd consider removing it and making it easier to reach out.
  • The "Subject" should probably be optional - it's asking too much.
  • Consider removing "(Optional)" label after "Screenshots" and "Application Logs" – it's obvious from the context.
  • The "Send" button is not visible until you scroll to he bottom. If you skip a required field like "Subject", reach the bottom and find it disabled, it's hard to figure out what's wrong. Consider using a Button.make(role: .confirm) in the trailing edge. It's always visible and provides immediate feedback.
  • Does it need a "Contact Information" section? I'd suggest adding this information on a confirmation screen if there is one – "we got your request. we will reach out to " etc.
  • It's be nice to pre-populate the "Site Address" field and use a site picker instead of asking you to type your site address manually.
  • I would suggest removing the "Attachment Limit" unless you reach the limit – then show it as an error. It also looks too much like a progress indicator, especially in things context. If you show it, consider showing a small label with text and no bar (excessive, take too much space and attention)

2. "Ask the Happiness Engineers"

I would make sure to consult with HEs before exposing this option directly. I would assume HEs would prefer a unified experience where you start with a bot that would gather the required information based on your query and get you in touch with support when needed.

Is there a reason we not using the same design as on the web with a single simple chat view? Is there a need for a dedicated "Contact Support" form in general? As a user, I would probably a chat-like interface and AI to ask me the questions if it needs more data instead of a large form to complete upfront.

"Ask the bots" – that's likely not something people normally would want to select given the option. If 95+% of people people skip this option and go to support directly, it could balloon the support cost and we would miss out on (great) AI assistant option.

3. Misc

  • The screen used to show the app version number, which is important
  • "Clearing Disk" got stuck showing both "Clearing..." and "Complete" at the same time
  • I'd suggest cutting the "Support Profile" view – it's just your account.
  • "Ask the bots" (if kept) should start with a new conversation and not your previous conversation history and not "No Conversations". "History" should be hidden somewhere in the navigation bar buttons.
Screenshot 2025-11-06 at 3 23 24 PM

let result = try await generator.image(at: CMTime(seconds: 0.0, preferredTimescale: 600))
let image = UIImage(cgImage: result.image)

guard let data = image.pngData() else {
Copy link
Contributor

Choose a reason for hiding this comment

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

It's relatively expensive to convert it to data and back. Does it needs to support data(for request). Would make sense to move it under image(for request)? Alternately, data(for request) could call image(for request) and then convert it to data so both would be supported.

/// Asynchronous Image View that replicates the public API of `SwiftUI.AsyncImage` to fetch
/// a video preview thumbnail.
/// It uses `ImageDownloader` to fetch and cache the images.
public struct CachedAsyncVideoPreview<Content>: View where Content: View {
Copy link
Contributor

Choose a reason for hiding this comment

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

It seems like a copy of AsyncImageKit.CachedAsyncImage with a single line that's different:

imageDownloader.image(for: ImageRequest(videoUrl: url))

I'd suggest adding an ImageRequest init in the existing CachedAsyncImage.

await MainActor.run {
withAnimation {
self.state = .clearing(progress: progress, result: "Working")
try await Task.runForAtLeast(.seconds(1.5)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

It seems excessive. Does it ever take more than 10ms to clear disk caches? Removing a cache folder is usually an instant operation. I'd suggest showing an indefinite ProgressView and eliminating the complexity with the reporting – less code, less stuff to localize and maintain.

}
}

public struct FullScreenErrorView: View {
Copy link
Contributor

Choose a reason for hiding this comment

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

Please use a standard EmptyStateView from WordPressUI and include the dependency if it's not yet included. Alternatively, you should now be able to use native ContentUnavailableView with iOS 17.

It looks like it's also missing localization.

.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top)
.padding(.top, 24)
.onChange(of: context.date, { oldValue, newValue in
if case .awaitingHiding(let until) = state {
Copy link
Contributor

@kean kean Nov 6, 2025

Choose a reason for hiding this comment

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

This seems overly complex. Other screens in the app use a simple understated ProgressView. You can instantly hide and show it and it looks good.

If it's loading the content, the progress view is shown in the middle. If it's a result an asynchronous action, it usually replaces a button. The overlays, and especially with custom design, make the app feel slow.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Is there an example of this somewhere? I couldn't find anything that had a "load the cache and display activity while updating from the server" pattern already, which was why I ended up adding this.

@GestureState private var currentZoom = 1.0

var magnification: some Gesture {
MagnifyGesture().updating($currentZoom, body: { newValue, state, transaction in
Copy link
Contributor

Choose a reason for hiding this comment

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

It didn't seem to work for me. The views are too small for zooming with gestures. I'd suggest using .contextMenu with a details view instead (for long-press) or removing it altogether.

@@ -0,0 +1,21 @@
import Foundation

public protocol CachedAndFetchedResult<T>: Sendable {
Copy link
Contributor

@kean kean Nov 6, 2025

Choose a reason for hiding this comment

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

(nit) This seems a bit confusing. What is UncachedResult for? Why does result have two async closures? Is it more like a promise? Why would you need CachedAndFetchedResult in a single struct?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, it's technically two promises – one for fetching from the cache and one for fetching from the network.

It allows you to return a single object from an endpoint that makes it easy to read the same type from the cache quickly then from the network.

Might need a better name, but it's a lot more ergonomic to work with from view code than AsyncStream<T>

@jkmassel
Copy link
Contributor Author

jkmassel commented Nov 7, 2025

Should use a standard Button.make(role: .cancel) (x mark) and in the leading edge instead of "Cancel" in the confirmation button placement.

Ah yeah that's just a miss.

The "I need help with" looks like it supports multiple selection but doesn't. It takes a lot of vertical space.
Is "I need help with" required and/or important? Is that something HEs care about? If not, I'd consider removing it and making it easier to reach out.

This was duplicated from other apps – I'm happy to get rid of it, it's just a UI for adding tags that may help the user. We'll let the HEs make that call.

The "Send" button is not visible until you scroll to he bottom. If you skip a required field like "Subject", reach the bottom and find it disabled, it's hard to figure out what's wrong. Consider using a Button.make(role: .confirm) in the trailing edge. It's always visible and provides immediate feedback.

Screenshot 2025-11-06 at 5 43 42 PM

I started with this, but here's what it looks like – can you tell just by looking at it if it's enabled or not (hint: it's disabled).

Does it need a "Contact Information" section? I'd suggest adding this information on a confirmation screen if there is one – "we got your request. we will reach out to " etc.

We take the user to the conversation detail view after this. We could make it less prominent if it turns out that's a big issue.

Is there a reason we not using the same design as on the web with a single simple chat view? Is there a need for a dedicated "Contact Support" form in general? As a user, I would probably a chat-like interface and AI to ask me the questions if it needs more data instead of a large form to complete upfront.

This is a direct request from the HEs – we have a chat-like interface now and it often lacks detail or just contains a word or two. It's not clear from the existing UI that you're filing a ticket – one of the main goals with this is to differentiate between chat and filing a ticket.

If 95+% of people people skip this option and go to support directly

If 95% of users don't want it to work this way, it probably shouldn't work that way? Only paying customers can access this anyway, so we shouldn't put a blocker in place. Also – this is how the current app works. There's "Contact support" (which takes you to the support bot) and there's "Tickets" which works the same way.

@sonarqubecloud
Copy link

sonarqubecloud bot commented Nov 7, 2025

enum RunForAtLeastResult<T>: Sendable where T: Sendable {
case result(T)
case wait
}
Copy link
Contributor

Choose a reason for hiding this comment

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

This enum is not used.

} label: {
ZStack {
if attachment.isImage {
AsyncImage(url: attachment.url) { image in
Copy link
Contributor

Choose a reason for hiding this comment

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

Use CachedAsyncImage?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants