Community Feedback: Sync Conflict Resolution UI Mockups

Hi everyone! :waving_hand:

As part of my GSoC project, I am working on building Interactive Sync Conflict Resolution for Joplin. The goal of this project is to make resolving sync conflicts much more easier by allowing users to review, compare, and resolve conflicts directly within the application, instead of manually comparing separate notes.

I have some UI mockups for both desktop and mobile, and I'd really appreciate your feedback before moving on to the implementation

The proposed workflow includes:

  • Automatic conflict resolution: for non-overlapping changes [ any additions or deletions] they will be merged automatically, so the users only need to review genuine conflicts

  • Interactive Diff View : This clearly highlights conflicting changes between the local copy and the latest synced version.
    :red_circle: Red: Your copy
    :green_circle: Green: The current working version, which starts as the latest synced version and updates as you resolve conflicts
    Darker highlights: The exact words that changed

  • Resolve conflicts one by one: If you prefer your local change, simply select "Use this version". Otherwise, keep the current version.

  • Conflict navigation (Previous / Next): to quickly move through conflicts in long notes

  • Editor mode: allowing users to manually edit the final resolved note [ your latest synced version] whenever neither version is exactly what they want.

  • Title conflict support: note title conflicts resolution is also included in the project

  • Consistent experience across desktop and mobile: Similar UI layout across the platforms and will be reusing Joplin's existing components wherever possible to keep the experience familiar

  • Undo / Redo support: throughout the resolution process, you can undo and redo from both the tabs. For example, the change made in diff view can be undo in editor mode as the functionality is shared

  • Live diff updates while editing: If you make changes in the Editor and switch back to Diff View, the diff is automatically recomputed against your latest edits. This makes sure that the diff always updates with the current state of the resolved note

  • Synchronized scrolling: When switching between Diff View and Editor, the editor automatically scrolls to the corresponding section you were reviewing, making it easier to continue editing without losing where you were reviewing before [ for long notes ]

Note: Automatic conflict resolution requires a common base version to safely merge non-overlapping changes. Some older conflict notes (or any conflict note without a common base) does not contain this information, so in that case automatic resolution cannot be performed. In these cases, all detected differences including non-overlapping additions and deletions will be displayed in the Diff View and can resolve them via Diff view or Editor

The attached mockups below shows:

  • Desktop and mobile layouts

  • Diff View and Editor mode

  • Title conflict

  • Small word-level conflict

  • Larger paragraph replacement

Any feedback is greatly appreciated. Thank you!

DESKTOP:

MOBILE:

maybe on mobile we can also think of something for the long paras, coz here the block is the whole paragraph both sides, so if like the para is big, red copy alone fills the screen and green is fully below, we cant easily compare as u can see in ss 3 of mobile, like to check "Fish" vs "Dolphin" you scroll up down and compare from memory

maybe for those long ones the para can show just once with both words inline at the changed spot, like Fish Dolphin, strikethrough for mine and green for current, like track changes in docs. for small ones like the title your blocks are cleaner. but overall this is cool

maybe for those long ones the para can show just once with both words inline at the changed spot, like Fish Dolphin, strikethrough for mine and green for current, like track changes in docs

The problem then is that if you want to apply the red version, it's very hard to visualise what you're actually applying. I do see your point about having to scroll alot for large paragraphs, but the word level diff within the red / green section does help alot with seeing what actually changed, and you can also switch over to the edit tab to see the whole of the green version any time, while looking at the red version.

I think any UI for resolving conflicts on a mobile screen size is going to have flaws, and its much more ideal to resolve conflicts on a larger screen. But I think this solution works really well for changes which are not too complicated. The diff view basically mimics the GitHub unified diff, which I regularly use to review PRs on my phone.

But Joplin likely contains a lot more prose and long paragraphs compared to github, which is mostly code. I think if a paragraph has a lot of changes, there is no good solution, but for example, a long paragraph with a small change at the beginning and a small change at the end should maybe be split into two reviewable sections?

Currently for the diffs, there is an automatically selected version (green "Current version") and the red version. What is the plan for when the user presses the "Use this version" button? Does it just swap the two (i.e. red becomes green and green becomes red with a button), or does is remove the diff sections? I think my preference would be to swap them, so the user has an easy undo, but I would like to hear other thoughts.

I think if a paragraph has a lot of changes, there is no good solution, but for example, a long paragraph with a small change at the beginning and a small change at the end should maybe be split into two reviewable sections?

yeah, I agree that reviewing these large paragraph changes in mobile is difficult, but splitting one big paragraph into independent conflict sections makes handling "use this version" & re-computing diff as per the edits in that section in editor tab is complex implementation wise and might introduce to new issues as well and also it will reduce the context around the conflict which eventually degrade the user experience

one approach I was thinking is about splitting them visually keeping the internal section handling same, let's say in a paragraph I have conflict at 3rd sentence and another conflict at 8th sentence ;then we can hide that unchanged lines inside a expandable tab something like this:

Conflict 1 [ sentence 1]
──────────────
⋯ Show unchanged content ⋯
─────────────
Conflict 2 [sentence 8]

Now both the conflicts will be in one-section [ either red or green ] itself with one "use this version" / "current version" ; if the user wants the more context to take the decision then he can use click that tab which shows the hidden text. same thing can be done using ellipses but there some flaws that need to be discussed on what how to determine the boundaries of the hidden, for example, should it be hidden between two conflicts, sentence boundaries, paragraphs, or any punctuation? each approach has it's different edge cases, especially for notes that usually don't follow a regular sentence structure

What is the plan for when the user presses the "Use this version" button? Does it just swap the two (i.e. red becomes green and green becomes red with a button), or does is remove the diff sections? I think my preference would be to swap them, so the user has an easy undo, but I would like to hear other thoughts.

Presently the plan is like, when the user clicks "Use this version" in red section, It replaces the green section in resolved note with red section [ not in diff Ui but in editor] and change the UI state from "Use this version" to text-only "Using this version" and there will be no changes in green and red section content until user made some edits via editor. I chose this model by considering the live diff computation, let's say the user clicks "use this version" and choses local over resolved and makes changes in local copy in editor, then in that case the diff will be between static local copy and the resolved note [ with local + some edits ] and at this time "Use this version" button will be enabled again

I do thought about swapping the "Use this version" ; maybe we can do something like this: instead of swapping the content or not showing the diff, only the labels/actions are swapped:
The selected version becomes “Current version” and the previously selected version now gets the “Use this version” button. this will allow easy undo, and for the above example case, if the user later edits that same section in the editor, this temporary action state will be dismissed. The diff is recomputed against the static local copy and the new working (resolved) version [ which is local + some edits now], so the UI returns to its normal state with:
Red: Local version (Use this version)
Green: Current working version (Current version)

This keeps the diff view consistent with the live diff(Local, Resolved) model but one concern is that the green section is meant to be consistently represent the current working (resolved) version, so if the labels/actions are temporarily swapped, users may still interpret the green section as “the resolved note” even though it is temporarily acting as the alternative version for undo, Iam not sure whether this will be good in practice

That's not my understanding of how it would work. If you replace the text in the resolved version with the red text, the chnge will disappear completely from the diff, because the versions will be the same. That is the automatic behaviour when you re-evaluate the diff. If you then make another change to that area in the edit view (or to any unchanged area), then a diff will show in the diff view again

@CalebJohn I think what would mitigate this significantly is adding the ability to sync conflicts, so that if the conflicts are too hard to resolve on mobile, you can resolve them later on a computer. On a small screen indeed it's hard to make any good solution. One of @laurent's earlier comments suggested he didn't want to deviate too far from 'standard' solutions used on other apps, so maybe rather than making a non-standard solution to attempt to make it better on mobile, more time should be focused to make conflicts able to be synced. I posted a couple pf thoughts about that on Please synchronize "Conflicts" folder - #2 by mrjo118

I also think if you try to re-invent the wheel to make diffs more readable, you're at risk of introducing bugs which cause rendering diffs incorrectly or causing a crash. Like I said, the current proposal follows GitHub style unified diff, which is quite standard and there are certainly libraries available which can create the internal representation for rendering this way

The 'use this version' button is a convenience for if you want to take the whole line, but if you wanted to take a section from the line only, you could select and copy the section you want in the diff view (in the red), switch to the edit view and paste it over the relevant part to replace. If you used word level 'word processor track changes' style diff like Keshav suggested, this could make it significantly hard to transfer over a block of text within the line if it is broken up by words

I'm not sure I understand the red/green local/remote/current UI presentation...
Also, you mentioned Editor mode, does this means there will be 3 possibilities: local, remote, current (different from local or remote) ?

@jacksonxly Thank you for your input. One of the key principles that led the direction of this design is that a 3 way merge view might be considered too complex for the average user that is not a programmer. Without 3 panels, it's difficult to implement 'keeping the decision out of the text'.

Outside of some users maybe finding it confusing, a 3 way merge view on a portrait mobile size screen just isn't going to work anyway? Do you have any suggestions for what might work for a UI which might be user friendly and fit well on a mobile screen?

There are only two versions being compared here in diff view:

  • Local: your original local note [ presently this note exists in conflicts notebook when conflict occurs in your device] , the conflicting sections in this note are shown in red section. The content in this never changes and is kept as a reference
  • Current: the note you are working on. It starts as the latest synced (remote) version, but once you make any changes, it becomes your own working version and is no longer exactly the remote version. This is shown in green section and keeps changing as you edit the note.

So there is no separate third version. The remote version is only the starting point of the current version. From there, the current version keeps changing as you edit it, and that's the version that will be saved when you click finish

@jacksonxly Thank you for taking time to propose your ideas, I think mrjo addressed the 3 panels approach limitations well, and regarding the usage of base: I completely agree that the base revision is mainly for the merge algorithm, not something that needs to be shown in the UI. That's actually we are implementing in this project, using a 3 way merge internally when a base is available, so only the remaining conflicts need to be appeared to the user in UI

Another thing is about one conflict at a time flow, I think it certainly simplifies the UI, but since Joplin notes are usually long documents, I am not sure whether users might find it harder to move back and forth between conflicts or understand the surrounding context compared to seeing the note as a whole

The two candidate versions stacked, with the word-level diff you already have. "Your version" and "Version from other device", not red and green.

I would like to know about this, can you share why you are not prefering red and green?

Two buttons: keep this one, keep the other. Plus an "edit" that drops into the editor seeded with whichever is selected.

The user never sees a diff of three things, never learns the words base, ours, theirs. They answer a sequence of small either/or questions. At the end, one preview of the resolved note before it is saved.
"Keeping the decision out of the text" also does not need extra panels. It is a state model, not a layout: each conflict holds a choice (mine / theirs / edited), and the resolved note is rendered from those choices. That is precisely what lets a user change their mind on conflict 1 after seeing conflict 3, which a mutate-the-text approach cannot do.
The honest cost of all this is that you need the base revision available at sync time. If Joplin cannot produce the last-synced revision for a note, the whole approach collapses back into two-way and the mockups you have are about as good as it gets.

Thanks for explaining your approach in more detail, this was actually quite close to earlier design we considered for this project. One of the main concerns, though, was that it still guides users through a conflict-resolution workflow rather than letting them work with the note naturally and this flow could become confusing for non-technical users, so we started exploring simpler alternatives

The current direction is to make the note itself the primary place where users work so that they can freely edit the note just like any normal joplin note, while the diff view acts as an assistant to explain the differences and provide quick actions ( like choosing the local version) wherever it is required for them

I'd be interested to hear your thoughts

(post deleted by author)

@jacksonxly That solution was basically what Varun originally proposed at the very beginning of the project. But he was asked to change the approach because it would provide a poor user experience due to not being able to see the context in the note for the conflict being reaolved. A view in note button was proposed, but overall it gives a disjointed solution. If managing conflicts as state, what is really needed is embedding the conflict resolution directly in the full note content. But the problem is if you apply your proposed solution but instead embed the resolution in the note, once you choose from the stacked options, the choice must disappear to show the result. Then you end up with the same problem that you have with the proposed approach in this thread, which is that you can't undo your choices with full flexibility.

As a compromise for the lack if seeing changes which were applied, the intention was to implement a shared undo stack between the diff view and the editor view, so you can undo choices which have been made (but this can only be undone by doing all changes back to this point).

The most feasible solution I see for embedding the choice into the full content while also allowing reverting any of your choices at any point is to include 2 diff views, which essentially means implementing a 3 way merge ui, regardless of whether that is done side by side or using tabs.

(post deleted by author)

@jacksonxly Ok, that sounds reasonable to be honest. I think this would work providing that you only make the conflict sections within the note editable and not the entire note. Otherwise it would be a nightmare to keep the conflict spans anchored and anchoring the correct place might be error prone.

However the problem then is if you do not show a diff, then it's really difficult to see changes to both versions on the same line, if the line is long. The word level diff in the screenshots of the OP really help with seeing what changed, and stacking both versions is still necessary when you click the edit button on one version, so that you have a reference to copy partial changes from.

So I think showing diffs via highlighting is still necessary, but it doesn't necessarily have to be red and green, it could be blue and green as an example. Also ideally the word level highlighting should still show for the version being edited when you click edit (and re-evaluate as you type) so that there is still a reference in that version too. For simplicity, if you decide to edit one version after starting editing the other version, maybe it should just bring up a prompt asking to confirm cancelling editing and that changes will be lost. That way you don't have to worry about storing and rendering additional edited states, unless the edit has been confirmed.

My suggestion is to implement a version that's quick and easy to implement first, and then try it out and improve it, instead of spending a lot of time debating and designing "ideal" version.