mirror of
https://gitlab.com/biskuteri-cafe/JKomasto2.git
synced 2024-11-20 04:04:51 +01:00
Added notifications window.
This commit is contained in:
parent
d546ef3210
commit
100f11c6e2
@ -28,6 +28,9 @@ JKomasto {
|
||||
private ImageWindow
|
||||
mediaWindow;
|
||||
|
||||
private NotificationsWindow
|
||||
notificationsWindow;
|
||||
|
||||
private TimelineWindowUpdater
|
||||
timelineWindowUpdater;
|
||||
|
||||
@ -45,6 +48,7 @@ JKomasto {
|
||||
autoViewWindow.setCursor(new Cursor(Cursor.WAIT_CURSOR));
|
||||
timelineWindow.setCursor(new Cursor(Cursor.WAIT_CURSOR));
|
||||
|
||||
notificationsWindow.showLatestPage();
|
||||
timelineWindow.showLatestPage();
|
||||
timelineWindow.setLocationByPlatform(true);
|
||||
timelineWindow.setVisible(true);
|
||||
@ -68,6 +72,9 @@ JKomasto {
|
||||
public ImageWindow
|
||||
getMediaWindow() { return mediaWindow; }
|
||||
|
||||
public NotificationsWindow
|
||||
getNotificationsWindow() { return notificationsWindow; }
|
||||
|
||||
// ---%-@-%---
|
||||
|
||||
public static void
|
||||
@ -85,11 +92,13 @@ JKomasto {
|
||||
autoViewWindow = new PostWindow(this);
|
||||
loginWindow = new LoginWindow(this);
|
||||
mediaWindow = new ImageWindow();
|
||||
notificationsWindow = new NotificationsWindow(this);
|
||||
|
||||
composeWindow.dispose();
|
||||
autoViewWindow.dispose();
|
||||
timelineWindow.dispose();
|
||||
mediaWindow.dispose();
|
||||
notificationsWindow.dispose();
|
||||
loginWindow.setLocationByPlatform(true);
|
||||
loginWindow.setVisible(true);
|
||||
|
||||
@ -116,12 +125,23 @@ TimelineType {
|
||||
FEDERATED,
|
||||
LOCAL,
|
||||
HOME,
|
||||
NOTIFICATIONS,
|
||||
CONVERSATIONS,
|
||||
LIST
|
||||
|
||||
}
|
||||
|
||||
enum
|
||||
NotificationType {
|
||||
|
||||
MENTION,
|
||||
BOOST,
|
||||
FAVOURITE,
|
||||
FOLLOW,
|
||||
FOLLOWREQ,
|
||||
POLL,
|
||||
ALERT
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
class
|
||||
@ -131,7 +151,7 @@ TimelinePage {
|
||||
type;
|
||||
|
||||
public String
|
||||
accountNumId;
|
||||
accountNumId, listId;
|
||||
|
||||
public List<Post>
|
||||
posts;
|
||||
@ -180,6 +200,20 @@ Post {
|
||||
|
||||
}
|
||||
|
||||
class
|
||||
Notification {
|
||||
|
||||
public NotificationType
|
||||
type;
|
||||
|
||||
public String
|
||||
id;
|
||||
|
||||
public String
|
||||
postId, postText, actorNumId, actorName;
|
||||
|
||||
}
|
||||
|
||||
|
||||
class
|
||||
Attachment {
|
||||
|
@ -178,29 +178,29 @@ MastodonApi {
|
||||
|
||||
public void
|
||||
getTimelinePage(
|
||||
TimelineType type, String accountId,
|
||||
TimelineType type,
|
||||
int count, String maxId, String minId,
|
||||
String accountId, String listId,
|
||||
RequestListener handler)
|
||||
{
|
||||
String token = accessToken.get("access_token").value;
|
||||
|
||||
assert !(accountId != null && listId != null);
|
||||
|
||||
String url = instanceUrl + "/api/v1";
|
||||
if (accountId != null)
|
||||
{
|
||||
url += "/accounts/" + accountId + "/statuses";
|
||||
}
|
||||
else if (listId != null)
|
||||
{
|
||||
url += "/lists/" + listId;
|
||||
}
|
||||
else switch (type)
|
||||
{
|
||||
case FEDERATED:
|
||||
case LOCAL: url += "/timelines/public"; break;
|
||||
case HOME: url += "/timelines/home"; break;
|
||||
case NOTIFICATIONS:
|
||||
url += "/notifications";
|
||||
// Note that this endpoint returns Notifications,
|
||||
// not Statuses. But we uniformly return Tree<String>,
|
||||
// we expect the caller can handle it.
|
||||
break;
|
||||
case CONVERSATIONS: url += "/timelines/public"; break;
|
||||
default: assert false;
|
||||
}
|
||||
url += "?limit=" + count;
|
||||
@ -325,6 +325,32 @@ MastodonApi {
|
||||
catch (IOException eIo) { handler.connectionFailed(eIo); }
|
||||
}
|
||||
|
||||
public void
|
||||
getNotifications(
|
||||
int count, String maxId, String minId,
|
||||
RequestListener handler)
|
||||
{
|
||||
String token = accessToken.get("access_token").value;
|
||||
|
||||
String url = instanceUrl + "/api/v1/notifications";
|
||||
url += "?limit=" + count;
|
||||
if (maxId != null) url += "&max_id=" + maxId;
|
||||
if (minId != null) url += "&min_id=" + minId;
|
||||
|
||||
try
|
||||
{
|
||||
URL endpoint = new URL(url);
|
||||
HttpURLConnection conn;
|
||||
conn = (HttpURLConnection)endpoint.openConnection();
|
||||
String s1 = "Bearer " + token;
|
||||
conn.setRequestProperty("Authorization", s1);
|
||||
conn.connect();
|
||||
|
||||
doStandardJsonReturn(conn, handler);
|
||||
}
|
||||
catch (IOException eIo) { handler.connectionFailed(eIo); }
|
||||
}
|
||||
|
||||
public void
|
||||
monitorTimeline(
|
||||
TimelineType type, ServerSideEventsListener handler)
|
||||
@ -336,8 +362,7 @@ MastodonApi {
|
||||
{
|
||||
case FEDERATED: url += "/public"; break;
|
||||
case LOCAL: url += "/public/local"; break;
|
||||
case HOME:
|
||||
case NOTIFICATIONS: url += "/user"; break;
|
||||
case HOME: url += "/user"; break;
|
||||
default: assert false;
|
||||
}
|
||||
|
||||
|
330
NotificationsWindow.java
Normal file
330
NotificationsWindow.java
Normal file
@ -0,0 +1,330 @@
|
||||
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.Box;
|
||||
import javax.swing.BoxLayout;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.border.Border;
|
||||
import java.awt.Font;
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.GridLayout;
|
||||
import java.awt.BorderLayout;
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.Cursor;
|
||||
import cafe.biskuteri.hinoki.Tree;
|
||||
import java.io.IOException;
|
||||
import java.awt.event.ComponentListener;
|
||||
import java.awt.event.ComponentEvent;
|
||||
|
||||
class
|
||||
NotificationsWindow extends JFrame {
|
||||
|
||||
private JKomasto
|
||||
primaire;
|
||||
|
||||
private List<Notification>
|
||||
notifications;
|
||||
|
||||
private MastodonApi
|
||||
api;
|
||||
|
||||
// - -%- -
|
||||
|
||||
private NotificationsComponent
|
||||
display;
|
||||
|
||||
// - -%- -
|
||||
|
||||
private static int
|
||||
ROW_COUNT = NotificationsComponent.ROW_COUNT;
|
||||
|
||||
// ---%-@-%---
|
||||
|
||||
public void
|
||||
showLatestPage()
|
||||
{
|
||||
fetchPage(null, null);
|
||||
display.showNotifications(notifications);
|
||||
}
|
||||
|
||||
public void
|
||||
showPrevPage()
|
||||
{
|
||||
assert !notifications.isEmpty();
|
||||
fetchPage(null, notifications.get(0).id);
|
||||
if (notifications.size() < ROW_COUNT) showLatestPage();
|
||||
display.showNotifications(notifications);
|
||||
}
|
||||
|
||||
public void
|
||||
showNextPage()
|
||||
{
|
||||
assert !notifications.isEmpty();
|
||||
int last = notifications.size() - 1;
|
||||
fetchPage(notifications.get(last).id, null);
|
||||
display.showNotifications(notifications);
|
||||
}
|
||||
|
||||
// - -%- -
|
||||
|
||||
private void
|
||||
fetchPage(String maxId, String minId)
|
||||
{
|
||||
display.setCursor(new Cursor(Cursor.WAIT_CURSOR));
|
||||
api.getNotifications(
|
||||
ROW_COUNT, maxId, minId,
|
||||
new RequestListener() {
|
||||
|
||||
public void
|
||||
connectionFailed(IOException eIo)
|
||||
{
|
||||
eIo.printStackTrace();
|
||||
}
|
||||
|
||||
public void
|
||||
requestFailed(int httpCode, Tree<String> json)
|
||||
{
|
||||
System.err.println(httpCode + json.get("error").value);
|
||||
}
|
||||
|
||||
public void
|
||||
requestSucceeded(Tree<String> json)
|
||||
{
|
||||
notifications = new ArrayList<>();
|
||||
for (Tree<String> t: json)
|
||||
{
|
||||
Notification n = new Notification();
|
||||
|
||||
n.id = t.get("id").value;
|
||||
|
||||
String type = t.get("type").value;
|
||||
if (type.equals("favourite"))
|
||||
n.type = NotificationType.FAVOURITE;
|
||||
else if (type.equals("reblog"))
|
||||
n.type = NotificationType.BOOST;
|
||||
else if (type.equals("mention"))
|
||||
n.type = NotificationType.MENTION;
|
||||
else if (type.equals("follow"))
|
||||
n.type = NotificationType.FOLLOW;
|
||||
else if (type.equals("follow_request"))
|
||||
n.type = NotificationType.FOLLOWREQ;
|
||||
else if (type.equals("poll"))
|
||||
n.type = NotificationType.POLL;
|
||||
else if (type.equals("status"))
|
||||
n.type = NotificationType.ALERT;
|
||||
|
||||
Tree<String> actor = t.get("account");
|
||||
String aid, aname, adisp;
|
||||
aid = actor.get("id").value;
|
||||
aname = actor.get("username").value;
|
||||
adisp = actor.get("display_name").value;
|
||||
if (!adisp.isEmpty()) n.actorName = adisp;
|
||||
else n.actorName = aname;
|
||||
n.actorNumId = aid;
|
||||
|
||||
if (n.type != NotificationType.FOLLOW)
|
||||
{
|
||||
Tree<String> post = t.get("status");
|
||||
String pid = post.get("id").value;
|
||||
String ptext = post.get("content").value;
|
||||
n.postId = pid;
|
||||
n.postText = ptext;
|
||||
// Should we ask TimelineWindow for help here?
|
||||
// Or should we break our text parsers into
|
||||
// a separate class?
|
||||
}
|
||||
|
||||
notifications.add(n);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
);
|
||||
display.setCursor(null);
|
||||
repaint();
|
||||
}
|
||||
|
||||
// ---%-@-%---
|
||||
|
||||
NotificationsWindow(JKomasto primaire)
|
||||
{
|
||||
super("Notifications");
|
||||
this.primaire = primaire;
|
||||
this.api = primaire.getMastodonApi();
|
||||
|
||||
notifications = new ArrayList<>();
|
||||
|
||||
display = new NotificationsComponent(this);
|
||||
display.setPreferredSize(new Dimension(256, 400));
|
||||
setContentPane(display);
|
||||
pack();
|
||||
|
||||
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
||||
setVisible(true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class
|
||||
NotificationsComponent extends JPanel
|
||||
implements ActionListener {
|
||||
|
||||
private NotificationsWindow
|
||||
primaire;
|
||||
|
||||
private JButton
|
||||
prev, next;
|
||||
|
||||
// - -%- -
|
||||
|
||||
private List<NotificationComponent>
|
||||
rows;
|
||||
|
||||
// - -%- -
|
||||
|
||||
static final int
|
||||
ROW_COUNT = 16;
|
||||
|
||||
// ---%-@-%---
|
||||
|
||||
public void
|
||||
showNotifications(List<Notification> notifications)
|
||||
{
|
||||
assert notifications.size() == rows.size();
|
||||
for (int o = 0; o < rows.size(); ++o)
|
||||
{
|
||||
Notification n = notifications.get(o);
|
||||
NotificationComponent c = rows.get(o);
|
||||
c.setName(n.actorName);
|
||||
c.setType(n.type.toString());
|
||||
c.setText(n.postText);
|
||||
}
|
||||
}
|
||||
|
||||
// - -%- -
|
||||
|
||||
public void
|
||||
actionPerformed(ActionEvent eA)
|
||||
{
|
||||
if (eA.getSource() == prev) primaire.showPrevPage();
|
||||
if (eA.getSource() == next) primaire.showNextPage();
|
||||
}
|
||||
|
||||
// ---%-@-%---
|
||||
|
||||
NotificationsComponent(NotificationsWindow primaire)
|
||||
{
|
||||
this.primaire = primaire;
|
||||
|
||||
Border b = BorderFactory.createEmptyBorder(8, 8, 8, 8);
|
||||
|
||||
rows = new ArrayList<>();
|
||||
for (int n = ROW_COUNT; n > 0; --n)
|
||||
rows.add(new NotificationComponent());
|
||||
|
||||
prev = new JButton("<");
|
||||
next = new JButton(">");
|
||||
prev.addActionListener(this);
|
||||
next.addActionListener(this);
|
||||
|
||||
JPanel centre = new JPanel();
|
||||
centre.setLayout(new GridLayout(ROW_COUNT, 1));
|
||||
for (NotificationComponent c: rows) centre.add(c);
|
||||
|
||||
Box bottom = Box.createHorizontalBox();
|
||||
bottom.add(Box.createGlue());
|
||||
bottom.add(prev);
|
||||
bottom.add(Box.createHorizontalStrut(8));
|
||||
bottom.add(next);
|
||||
bottom.setBorder(b);
|
||||
|
||||
setLayout(new BorderLayout());
|
||||
add(centre);
|
||||
add(bottom, BorderLayout.SOUTH);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class
|
||||
NotificationComponent extends JComponent
|
||||
implements ComponentListener {
|
||||
|
||||
private JLabel
|
||||
type;
|
||||
|
||||
private JLabel
|
||||
name, text;
|
||||
|
||||
// ---%-@-%---
|
||||
|
||||
public void
|
||||
setType(String n) { type.setText(n); }
|
||||
|
||||
public void
|
||||
setName(String n) { name.setText(n); }
|
||||
|
||||
public void
|
||||
setText(String n) { text.setText(n); }
|
||||
|
||||
// - -%- -
|
||||
|
||||
public void
|
||||
componentResized(ComponentEvent eC)
|
||||
{
|
||||
int w = getWidth(), h = getHeight();
|
||||
name.setPreferredSize(new Dimension(w * 4 / 10, h));
|
||||
type.setPreferredSize(new Dimension(w * 3 / 10, h));
|
||||
text.setPreferredSize(new Dimension(w * 2 / 10, h));
|
||||
|
||||
name.setMaximumSize(new Dimension(w * 4 / 10, h));
|
||||
type.setMaximumSize(new Dimension(w * 3 / 10, h));
|
||||
text.setMaximumSize(new Dimension(w * 2 / 10, h));
|
||||
}
|
||||
|
||||
public void
|
||||
componentShown(ComponentEvent eC) { }
|
||||
|
||||
public void
|
||||
componentHidden(ComponentEvent eC) { }
|
||||
|
||||
public void
|
||||
componentMoved(ComponentEvent eC) { }
|
||||
|
||||
// ---%-@-%---
|
||||
|
||||
NotificationComponent()
|
||||
{
|
||||
Font f1 = new Font("Dialog", Font.PLAIN, 12);
|
||||
Font f2 = new Font("Dialog", Font.PLAIN, 10);
|
||||
Font f3 = new Font("Dialog", Font.ITALIC, 12);
|
||||
|
||||
name = new JLabel();
|
||||
type = new JLabel();
|
||||
text = new JLabel();
|
||||
name.setFont(f1);
|
||||
type.setFont(f2);
|
||||
text.setFont(f3);
|
||||
type.setHorizontalAlignment(JLabel.RIGHT);
|
||||
|
||||
setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
|
||||
add(name);
|
||||
add(Box.createHorizontalStrut(4));
|
||||
add(type);
|
||||
add(Box.createHorizontalStrut(4));
|
||||
add(text);
|
||||
|
||||
this.addComponentListener(this);
|
||||
setBorder(
|
||||
BorderFactory.createMatteBorder
|
||||
(1, 0, 0, 0, new Color(0, 0, 0, 25))
|
||||
);
|
||||
}
|
||||
|
||||
}
|
@ -65,6 +65,7 @@ implements ActionListener {
|
||||
openMessages,
|
||||
openLocal,
|
||||
openFederated,
|
||||
openNotifications,
|
||||
createPost,
|
||||
openAutoPostView,
|
||||
quit;
|
||||
@ -113,8 +114,9 @@ implements ActionListener {
|
||||
{
|
||||
display.setCursor(new Cursor(Cursor.WAIT_CURSOR));
|
||||
api.getTimelinePage(
|
||||
page.type, page.accountNumId,
|
||||
PREVIEW_COUNT, null, null,
|
||||
page.type,
|
||||
PREVIEW_COUNT, null, null,
|
||||
page.accountNumId, page.listId,
|
||||
new RequestListener() {
|
||||
|
||||
public void
|
||||
@ -159,8 +161,9 @@ implements ActionListener {
|
||||
|
||||
display.setCursor(new Cursor(Cursor.WAIT_CURSOR));
|
||||
api.getTimelinePage(
|
||||
page.type, page.accountNumId,
|
||||
PREVIEW_COUNT, last.postId, null,
|
||||
page.type,
|
||||
PREVIEW_COUNT, last.postId, null,
|
||||
page.accountNumId, page.listId,
|
||||
new RequestListener() {
|
||||
|
||||
public void
|
||||
@ -208,8 +211,9 @@ implements ActionListener {
|
||||
|
||||
display.setCursor(new Cursor(Cursor.WAIT_CURSOR));
|
||||
api.getTimelinePage(
|
||||
page.type, page.accountNumId,
|
||||
PREVIEW_COUNT, null, first.postId,
|
||||
page.type,
|
||||
PREVIEW_COUNT, null, first.postId,
|
||||
page.accountNumId, page.listId,
|
||||
new RequestListener() {
|
||||
|
||||
public void
|
||||
@ -291,6 +295,12 @@ implements ActionListener {
|
||||
PostWindow w = primaire.getAutoViewWindow();
|
||||
w.setLocation(getX() + 10 + getWidth(), getY());
|
||||
w.setVisible(true);
|
||||
}
|
||||
if (src == openNotifications)
|
||||
{
|
||||
NotificationsWindow w = primaire.getNotificationsWindow();
|
||||
w.setLocationByPlatform(true);
|
||||
w.setVisible(true);
|
||||
}
|
||||
if (src == flipToNewestPost)
|
||||
{
|
||||
@ -447,11 +457,13 @@ implements ActionListener {
|
||||
|
||||
openHome = new JMenuItem("Open home timeline");
|
||||
openFederated = new JMenuItem("Open federated timeline");
|
||||
openNotifications = new JMenuItem("Open notifications");
|
||||
createPost = new JMenuItem("Create a post");
|
||||
openAutoPostView = new JMenuItem("Open auto post view");
|
||||
quit = new JMenuItem("Quit");
|
||||
openHome.addActionListener(this);
|
||||
openFederated.addActionListener(this);
|
||||
openNotifications.addActionListener(this);
|
||||
createPost.addActionListener(this);
|
||||
openAutoPostView.addActionListener(this);
|
||||
quit.addActionListener(this);
|
||||
@ -463,6 +475,7 @@ implements ActionListener {
|
||||
programMenu.setMnemonic(KeyEvent.VK_P);
|
||||
programMenu.add(openHome);
|
||||
programMenu.add(openFederated);
|
||||
programMenu.add(openNotifications);
|
||||
programMenu.add(new JSeparator());
|
||||
programMenu.add(createPost);
|
||||
programMenu.add(openAutoPostView);
|
||||
|
@ -26,8 +26,7 @@ TimelineWindowUpdater {
|
||||
private Thread
|
||||
federated,
|
||||
local,
|
||||
home,
|
||||
notifications;
|
||||
home;
|
||||
|
||||
// ---%-@-%---
|
||||
|
||||
@ -49,9 +48,6 @@ TimelineWindowUpdater {
|
||||
case HOME:
|
||||
if (home != null) return;
|
||||
home = t; break;
|
||||
case NOTIFICATIONS:
|
||||
if (notifications != null) return;
|
||||
notifications = t; break;
|
||||
default: return;
|
||||
}
|
||||
t.start();
|
||||
|
Loading…
Reference in New Issue
Block a user