Added delete feature

This commit is contained in:
Snowyfox 2022-04-27 21:56:25 -04:00
parent 26d4e8c97a
commit 0590cc6c19
6 changed files with 246 additions and 54 deletions

View File

@ -51,7 +51,7 @@ ComposeWindow extends JFrame {
{ {
composition = new Composition(); composition = new Composition();
composition.text = ""; composition.text = "";
composition.visibility = PostVisibility.MENTIONED; composition.visibility = PostVisibility.UNLISTED;
composition.replyToPostId = null; composition.replyToPostId = null;
composition.contentWarning = null; composition.contentWarning = null;
syncDisplayToComposition(); syncDisplayToComposition();

View File

@ -351,6 +351,27 @@ MastodonApi {
catch (IOException eIo) { handler.connectionFailed(eIo); } catch (IOException eIo) { handler.connectionFailed(eIo); }
} }
public void
deletePost(String postId, RequestListener handler)
{
String token = accessToken.get("access_token").value;
String url = instanceUrl + "/api/v1/statuses/" + postId;
try
{
URL endpoint = new URL(url);
HttpURLConnection conn;
conn = (HttpURLConnection)endpoint.openConnection();
String s1 = "Bearer " + token;
conn.setRequestProperty("Authorization", s1);
conn.setRequestMethod("DELETE");
conn.connect();
doStandardJsonReturn(conn, handler);
}
catch (IOException eIo) { handler.connectionFailed(eIo); }
}
public void public void
getAccounts(String query, RequestListener handler) getAccounts(String query, RequestListener handler)
{ {

View File

@ -132,13 +132,14 @@ NotificationsWindow extends JFrame {
if (n.type != NotificationType.FOLLOW) if (n.type != NotificationType.FOLLOW)
{ {
Tree<String> post = t.get("status"); Tree<String> post = t.get("status");
String pid = post.get("id").value; String pid, phtml, ptext;
String ptext = post.get("content").value; pid = post.get("id").value;
phtml = post.get("content").value;
ptext =
TimelineComponent
.textApproximation(phtml);
n.postId = pid; n.postId = pid;
n.postText = ptext; n.postText = ptext;
// Should we ask TimelineWindow for help here?
// Or should we break our text parsers into
// a separate class?
} }
notifications.add(n); notifications.add(n);
@ -162,7 +163,7 @@ NotificationsWindow extends JFrame {
notifications = new ArrayList<>(); notifications = new ArrayList<>();
display = new NotificationsComponent(this); display = new NotificationsComponent(this);
display.setPreferredSize(new Dimension(256, 400)); display.setPreferredSize(new Dimension(256, 260));
setContentPane(display); setContentPane(display);
pack(); pack();
@ -190,7 +191,7 @@ implements ActionListener {
// - -%- - // - -%- -
static final int static final int
ROW_COUNT = 16; ROW_COUNT = 10;
// ---%-@-%--- // ---%-@-%---
@ -203,7 +204,16 @@ implements ActionListener {
Notification n = notifications.get(o); Notification n = notifications.get(o);
NotificationComponent c = rows.get(o); NotificationComponent c = rows.get(o);
c.setName(n.actorName); c.setName(n.actorName);
c.setType(n.type.toString()); switch (n.type)
{
case MENTION: c.setType("mentioned"); break;
case BOOST: c.setType("boosted"); break;
case FAVOURITE: c.setType("favourited"); break;
case FOLLOW: c.setType("followed"); break;
case FOLLOWREQ: c.setType("req. follow"); break;
case POLL: c.setType("poll ended"); break;
case ALERT: c.setType("posted"); break;
}
c.setText(n.postText); c.setText(n.postText);
} }
} }
@ -279,13 +289,13 @@ implements ComponentListener {
componentResized(ComponentEvent eC) componentResized(ComponentEvent eC)
{ {
int w = getWidth(), h = getHeight(); int w = getWidth(), h = getHeight();
name.setPreferredSize(new Dimension(w * 4 / 10, h)); name.setPreferredSize(new Dimension(w * 7 / 20, h));
type.setPreferredSize(new Dimension(w * 3 / 10, h)); type.setPreferredSize(new Dimension(w * 6 / 20, h));
text.setPreferredSize(new Dimension(w * 2 / 10, h)); text.setPreferredSize(new Dimension(w * 5 / 20, h));
name.setMaximumSize(new Dimension(w * 4 / 10, h)); name.setMaximumSize(new Dimension(w * 7 / 20, h));
type.setMaximumSize(new Dimension(w * 3 / 10, h)); type.setMaximumSize(new Dimension(w * 6 / 20, h));
text.setMaximumSize(new Dimension(w * 2 / 10, h)); text.setMaximumSize(new Dimension(w * 5 / 20, h));
} }
public void public void
@ -301,9 +311,13 @@ implements ComponentListener {
NotificationComponent() NotificationComponent()
{ {
Font f1 = new Font("Dialog", Font.PLAIN, 12); Font f = new Font("Dialog", Font.PLAIN, 12);
Font f2 = new Font("Dialog", Font.PLAIN, 10); Font f1 = f.deriveFont(Font.PLAIN, 14);
Font f3 = new Font("Dialog", Font.ITALIC, 12); Font f2 = f.deriveFont(Font.PLAIN, 11);
Font f3 = f.deriveFont(Font.ITALIC, 14);
Color c = new Color(0, 0, 0, 25);
Border b1 = BorderFactory.createMatteBorder(0, 0, 1, 0, c);
name = new JLabel(); name = new JLabel();
type = new JLabel(); type = new JLabel();
@ -314,17 +328,16 @@ implements ComponentListener {
type.setHorizontalAlignment(JLabel.RIGHT); type.setHorizontalAlignment(JLabel.RIGHT);
setLayout(new BoxLayout(this, BoxLayout.X_AXIS)); setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
add(Box.createHorizontalStrut(4));
add(name); add(name);
add(Box.createHorizontalStrut(4)); add(Box.createHorizontalStrut(8));
add(type); add(type);
add(Box.createHorizontalStrut(4)); add(Box.createHorizontalStrut(8));
add(text); add(text);
add(Box.createHorizontalStrut(4));
this.addComponentListener(this); this.addComponentListener(this);
setBorder( setBorder(b1);
BorderFactory.createMatteBorder
(1, 0, 0, 0, new Color(0, 0, 0, 25))
);
} }
} }

View File

@ -2,7 +2,7 @@
import javax.swing.JFrame; import javax.swing.JFrame;
import javax.swing.JPanel; import javax.swing.JPanel;
import javax.swing.JMenuBar; import javax.swing.JMenuBar;
import javax.swing.JMenu; import javax.swing.JPopupMenu;
import javax.swing.JMenuItem; import javax.swing.JMenuItem;
import javax.swing.JButton; import javax.swing.JButton;
import javax.swing.JLabel; import javax.swing.JLabel;
@ -86,6 +86,10 @@ implements ActionListener {
postDisplay.setAuthorName(an); postDisplay.setAuthorName(an);
postDisplay.setAuthorId(author.get("acct").value); postDisplay.setAuthorId(author.get("acct").value);
String aid = author.get("id").value;
String oid = api.getAccountDetails().get("id").value;
postDisplay.setDeleteEnabled(aid.equals(oid));
String avurl = author.get("avatar").value; String avurl = author.get("avatar").value;
postDisplay.setAuthorAvatar(ImageApi.remote(avurl)); postDisplay.setAuthorAvatar(ImageApi.remote(avurl));
@ -255,7 +259,7 @@ implements ActionListener {
if (vs.equals("unlisted")) v = PostVisibility.UNLISTED; if (vs.equals("unlisted")) v = PostVisibility.UNLISTED;
if (vs.equals("private")) v = PostVisibility.FOLLOWERS; if (vs.equals("private")) v = PostVisibility.FOLLOWERS;
if (vs.equals("direct")) v = PostVisibility.MENTIONED; if (vs.equals("direct")) v = PostVisibility.MENTIONED;
Composition c = new Composition(); Composition c = new Composition();
c.contentWarning = cw; c.contentWarning = cw;
c.text = id1.equals(id2) ? "" : "@" + authorId + " "; c.text = id1.equals(id2) ? "" : "@" + authorId + " ";
@ -300,6 +304,112 @@ implements ActionListener {
} }
} }
public void
deletePost(boolean redraft)
{
postDisplay.setCursor(new Cursor(Cursor.WAIT_CURSOR));
postDisplay.setDeleteEnabled(false);
postDisplay.paintImmediately(postDisplay.getBounds());
if (redraft)
{
String html = post.get("content").value;
StringBuilder b = new StringBuilder();
Tree<String> nodes;
nodes = RudimentaryHTMLParser.depthlessRead(html);
// We have to salvage whatever we can from the HTML.
for (Tree<String> node: nodes)
{
if (node.key.equals("text"))
{
b.append(node.value);
}
if (node.key.equals("emoji"))
{
b.append(":" + node.value + ":");
}
if (node.key.equals("tag"))
{
if (node.get(0).key.equals("/p"))
b.append("\n\n");
if (node.get(0).key.equals("br"))
b.append("\n");
if (node.get(0).key.equals("a")) {
b.append(node.get("href").value);
b.append(" ");
continue;
/*
* We don't omit the contents of the <a>
* which is an automatic label, but we'll
* need a non-depthless parser to omit that.
* For now prioritise not losing anything
* from our composition.
*/
}
// I think that's all. I hope.
}
}
String cw = post.get("spoiler_text").value;
String vs = post.get("visibility").value;
PostVisibility v = null;
if (vs.equals("public")) v = PostVisibility.PUBLIC;
if (vs.equals("unlisted")) v = PostVisibility.UNLISTED;
if (vs.equals("private")) v = PostVisibility.FOLLOWERS;
if (vs.equals("direct")) v = PostVisibility.MENTIONED;
String replyTo = post.get("in_reply_to_id").value;
Composition c = new Composition();
c.contentWarning = cw;
c.text = b.toString();
c.visibility = v;
c.replyToPostId = replyTo;
ComposeWindow w = primaire.getComposeWindow();
w.setLocation(getX(), getY() + 100);
w.setVisible(true);
w.setComposition(c);
}
api.deletePost(post.get("id").value, new RequestListener() {
public void
connectionFailed(IOException eIo)
{
JOptionPane.showMessageDialog(
PostWindow.this,
"Failed to delete post.."
+ "\n" + eIo.getMessage()
);
}
public void
requestFailed(int httpCode, Tree<String> json)
{
JOptionPane.showMessageDialog(
PostWindow.this,
"Failed to delete post.."
+ "\n" + json.get("error").value
+ "\n(HTTP code: " + httpCode + ")"
);
}
public void
requestSucceeded(Tree<String> json)
{
setVisible(false);
}
});
postDisplay.setCursor(null);
postDisplay.setDeleteEnabled(true);
postDisplay.paintImmediately(postDisplay.getBounds());
if (!isVisible()) dispose();
}
// - -%- - // - -%- -
public void public void
@ -379,6 +489,13 @@ implements ActionListener {
profile, profile,
media; media;
private JPopupMenu
miscMenu;
private JMenuItem
deletePost,
redraftPost;
// ---%-@-%--- // ---%-@-%---
public void public void
@ -475,6 +592,13 @@ implements ActionListener {
favouriteBoost.setEnabled(a); favouriteBoost.setEnabled(a);
} }
public void
setDeleteEnabled(boolean a)
{
deletePost.setEnabled(a);
redraftPost.setEnabled(a);
}
public void public void
setMediaPreview(Image n) { media.setImage(n); } setMediaPreview(Image n) { media.setImage(n); }
@ -510,7 +634,14 @@ implements ActionListener {
if (src == replyMisc) if (src == replyMisc)
{ {
if (command.startsWith("reply")) primaire.reply(); if (command.startsWith("reply"))
primaire.reply();
if (command.startsWith("misc"))
{
int rx = replyMisc.getWidth() / 2;
int ry = replyMisc.getHeight() - miscMenu.getHeight();
miscMenu.show(replyMisc, rx, ry);
}
return; return;
} }
@ -531,7 +662,20 @@ implements ActionListener {
{ {
primaire.openMedia(); primaire.openMedia();
return; return;
} }
if (src == deletePost)
{
primaire.deletePost(false);
return;
}
if (src == redraftPost)
{
primaire.deletePost(true);
return;
}
} }
protected void protected void
@ -584,6 +728,14 @@ implements ActionListener {
nextPrev.addActionListener(this); nextPrev.addActionListener(this);
media.addActionListener(this); media.addActionListener(this);
deletePost = new JMenuItem("Delete post");
redraftPost = new JMenuItem("Delete and redraft post");
deletePost.addActionListener(this);
redraftPost.addActionListener(this);
miscMenu = new JPopupMenu();
miscMenu.add(deletePost);
miscMenu.add(redraftPost);
Box buttons = Box.createVerticalBox(); Box buttons = Box.createVerticalBox();
buttons.setOpaque(false); buttons.setOpaque(false);
buttons.add(profile); buttons.add(profile);

View File

@ -26,26 +26,6 @@ RudimentaryHTMLParser {
} }
} }
public static String
stripTags(String html)
{
StringBuilder returnee = new StringBuilder();
for (Tree<String> node: depthlessRead(html))
{
if (node.key.equals("tag")) continue;
if (node.key.equals("emoji"))
{
returnee.append(":" + node.value + ":");
}
if (node.key.equals("text"))
{
returnee.append(node.value);
}
}
return returnee.toString();
}
// - -%- - // - -%- -
private static Tree<String> private static Tree<String>

View File

@ -450,7 +450,7 @@ implements ActionListener {
} }
} }
// - -%- - // - -%- -
private static String private static String
plainify(String html) plainify(String html)
@ -620,12 +620,10 @@ implements
String html = p.get("content").value; String html = p.get("content").value;
String cw = p.get("spoiler_text").value; String cw = p.get("spoiler_text").value;
if (!cw.isEmpty()) { if (!cw.isEmpty())
c.setBottom("(" + cw + ")"); c.setBottom("(" + cw + ")");
} else
else { c.setBottom(textApproximation(html) + " ");
c.setBottom(RudimentaryHTMLParser.stripTags(html) + " ");
}
} }
for (int o = posts.size(); o < postPreviews.size(); ++o) for (int o = posts.size(); o < postPreviews.size(); ++o)
{ {
@ -781,6 +779,34 @@ implements
public void public void
keyReleased(KeyEvent eK) { } keyReleased(KeyEvent eK) { }
// - -%- -
public static String
textApproximation(String html)
{
StringBuilder returnee = new StringBuilder();
Tree<String> nodes = RudimentaryHTMLParser.depthlessRead(html);
Tree<String> first = nodes.get(0);
for (Tree<String> node: nodes)
{
if (node.key.equals("tag"))
{
if (node.get(0).key.equals("br"))
returnee.append("; ");
if (node.get(0).key.equals("p") && node != first)
returnee.append("; ");
}
if (node.key.equals("emoji"))
{
returnee.append(":" + node.value + ":");
}
if (node.key.equals("text"))
{
returnee.append(node.value);
}
}
return returnee.toString();
}
// ---%-@-%--- // ---%-@-%---
TimelineComponent(TimelineWindow primaire) TimelineComponent(TimelineWindow primaire)