feat: display edit history diff (#922)
* build: add google's diff-match-patch
Copied from 62f2e689f4/java/src/name/fraser/neil/plaintext/diff_match_patch.java
* feat(status/edit-history): display diff for text
Closes https://github.com/sk22/megalodon/issues/789
* fix(status/edit-history): add fake poll id
* code style adjustments
* don't diff if only formatting changed
---------
Co-authored-by: sk <sk22@mailbox.org>
This commit is contained in:
@@ -26,6 +26,8 @@ public class GetStatusEditHistory extends MastodonAPIRequest<List<Status>>{
|
||||
s.visibility=StatusPrivacy.PUBLIC;
|
||||
s.mentions=Collections.emptyList();
|
||||
s.tags=Collections.emptyList();
|
||||
if (s.poll != null)
|
||||
s.poll.id="fakeID"+i;
|
||||
i++;
|
||||
}
|
||||
super.validateAndPostprocessResponse(respObj, httpResponse);
|
||||
|
||||
@@ -12,18 +12,22 @@ import org.joinmastodon.android.model.Status;
|
||||
import org.joinmastodon.android.ui.displayitems.DummyStatusDisplayItem;
|
||||
import org.joinmastodon.android.ui.displayitems.ReblogOrReplyLineStatusDisplayItem;
|
||||
import org.joinmastodon.android.ui.displayitems.StatusDisplayItem;
|
||||
import org.joinmastodon.android.ui.text.HtmlParser;
|
||||
import org.joinmastodon.android.ui.utils.InsetStatusItemDecoration;
|
||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||
|
||||
import java.time.ZoneId;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.EnumSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import me.grishka.appkit.api.SimpleCallback;
|
||||
import name.fraser.neil.plaintext.diff_match_patch;
|
||||
|
||||
public class StatusEditHistoryFragment extends StatusListFragment{
|
||||
private String id, url;
|
||||
@@ -58,7 +62,7 @@ public class StatusEditHistoryFragment extends StatusListFragment{
|
||||
|
||||
@Override
|
||||
protected List<StatusDisplayItem> buildDisplayItems(Status s){
|
||||
List<StatusDisplayItem> items=StatusDisplayItem.buildItems(this, s, accountID, s, knownAccounts, null, StatusDisplayItem.FLAG_NO_FOOTER | StatusDisplayItem.FLAG_INSET | StatusDisplayItem.FLAG_NO_EMOJI_REACTIONS);
|
||||
List<StatusDisplayItem> items=new ArrayList<>();
|
||||
int idx=data.indexOf(s);
|
||||
if(idx>=0){
|
||||
String date=UiUtils.DATE_TIME_FORMATTER.format(s.createdAt.atZone(ZoneId.systemDefault()));
|
||||
@@ -83,8 +87,11 @@ public class StatusEditHistoryFragment extends StatusListFragment{
|
||||
EnumSet<StatusEditChangeType> changes=EnumSet.noneOf(StatusEditChangeType.class);
|
||||
Status prev=data.get(idx+1);
|
||||
|
||||
if(!Objects.equals(s.content, prev.content)){
|
||||
// if only formatting was changed, don't even try to create a diff text
|
||||
if(!Objects.equals(HtmlParser.text(s.content), HtmlParser.text(prev.content))){
|
||||
changes.add(StatusEditChangeType.TEXT_CHANGED);
|
||||
//update status content to display a diffs
|
||||
s.content=createDiffText(prev.content, s.content);
|
||||
}
|
||||
if(!Objects.equals(s.spoilerText, prev.spoilerText)){
|
||||
if(s.spoilerText==null){
|
||||
@@ -147,6 +154,7 @@ public class StatusEditHistoryFragment extends StatusListFragment{
|
||||
items.add(0, new ReblogOrReplyLineStatusDisplayItem(s.id, this, action+" "+sep+" "+date, Collections.emptyList(), 0, null, null, s));
|
||||
items.add(1, new DummyStatusDisplayItem(s.id, this));
|
||||
}
|
||||
items.addAll(StatusDisplayItem.buildItems(this, s, accountID, s, knownAccounts, null, StatusDisplayItem.FLAG_NO_FOOTER|StatusDisplayItem.FLAG_INSET|StatusDisplayItem.FLAG_NO_EMOJI_REACTIONS));
|
||||
return items;
|
||||
}
|
||||
|
||||
@@ -170,4 +178,28 @@ public class StatusEditHistoryFragment extends StatusListFragment{
|
||||
public Uri getWebUri(Uri.Builder base) {
|
||||
return Uri.parse(url);
|
||||
}
|
||||
|
||||
private String createDiffText(String original, String modified) {
|
||||
diff_match_patch dmp=new diff_match_patch();
|
||||
LinkedList<diff_match_patch.Diff> diffs=dmp.diff_main(original, modified);
|
||||
dmp.diff_cleanupSemantic(diffs);
|
||||
|
||||
StringBuilder stringBuilder=new StringBuilder();
|
||||
for(diff_match_patch.Diff diff : diffs){
|
||||
switch(diff.operation){
|
||||
case DELETE->{
|
||||
stringBuilder.append("<edit-diff-delete>");
|
||||
stringBuilder.append(diff.text);
|
||||
stringBuilder.append("</edit-diff-delete>");
|
||||
}
|
||||
case INSERT->{
|
||||
stringBuilder.append("<edit-diff-insert>");
|
||||
stringBuilder.append(diff.text);
|
||||
stringBuilder.append("</edit-diff-insert>");
|
||||
}
|
||||
default->stringBuilder.append(diff.text);
|
||||
}
|
||||
}
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -264,7 +264,7 @@ public class Status extends BaseModel implements DisplayItemsParent, Searchable{
|
||||
s.visibility=StatusPrivacy.PUBLIC;
|
||||
s.reactions=List.of();
|
||||
s.mentions=List.of();
|
||||
s.tags =List.of();
|
||||
s.tags=List.of();
|
||||
s.emojis=List.of();
|
||||
s.filtered=List.of();
|
||||
return s;
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
package org.joinmastodon.android.ui.text;
|
||||
|
||||
import android.text.TextPaint;
|
||||
import android.text.style.CharacterStyle;
|
||||
|
||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||
|
||||
public class DiffRemovedSpan extends CharacterStyle {
|
||||
|
||||
private final String text;
|
||||
|
||||
public DiffRemovedSpan(String text){
|
||||
this.text=text;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void updateDrawState(TextPaint tp) {
|
||||
tp.setStrikeThruText(true);
|
||||
tp.setColor(0xFFCA5B63);
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
return text;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.joinmastodon.android.ui.text;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Typeface;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.Spanned;
|
||||
@@ -171,6 +172,9 @@ public class HtmlParser{
|
||||
}
|
||||
case "code", "pre" -> openSpans.add(new SpanInfo(new TypefaceSpan("monospace"), ssb.length(), el));
|
||||
case "blockquote" -> openSpans.add(new SpanInfo(new LeadingMarginSpan.Standard(V.dp(10)), ssb.length(), el));
|
||||
// fake elements for the edit history diff view
|
||||
case "edit-diff-insert" -> openSpans.add(new SpanInfo(new ForegroundColorSpan(UiUtils.isDarkTheme() ? 0xFF89bb9c : 0xFF5b8e63), ssb.length(), el));
|
||||
case "edit-diff-delete" -> openSpans.add(new SpanInfo(new DiffRemovedSpan(el.text()), ssb.length(), el));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user