biskuteri-cafe-JKomasto2/NotificationsWindow.java

481 lines
12 KiB
Java

/* copyright
This file is part of JKomasto2.
Written in 2022 by Usawashi <usawashi16@yahoo.co.jp>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
copyright */
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.awt.Image;
import java.awt.Graphics;
import java.util.List;
import java.util.ArrayList;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.Cursor;
import java.awt.Insets;
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 boolean
showingLatest;
// - -%- -
private static int
ROW_COUNT = NotificationsComponent.ROW_COUNT;
// ---%-@-%---
public synchronized void
readEntity(Tree<String> entity)
{
notifications = new ArrayList<>();
for (Tree<String> t: entity)
{
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)
{
Post post = new Post(t.get("status"));
post.resolveApproximateText();
n.postId = post.id;
n.postText = post.approximateText;
}
notifications.add(n);
}
}
public void
refresh()
{
String firstId = null;
if (!showingLatest)
{
assert !notifications.isEmpty();
firstId = notifications.get(0).id;
}
if (fetchPage(firstId, null))
{
if (notifications.size() < ROW_COUNT) showLatestPage();
display.showNotifications(notifications);
display.repaint();
}
}
public void
showLatestPage()
{
if (fetchPage(null, null))
{
display.showNotifications(notifications);
showingLatest = true;
primaire.getWindowUpdater().add(this);
}
}
public void
showPrevPage()
{
assert !notifications.isEmpty();
if (fetchPage(null, notifications.get(0).id))
{
if (notifications.size() < ROW_COUNT) showLatestPage();
display.showNotifications(notifications);
showingLatest = false;
primaire.getWindowUpdater().remove(this);
}
}
public void
showNextPage()
{
assert !notifications.isEmpty();
int last = notifications.size() - 1;
if (fetchPage(notifications.get(last).id, null))
{
display.showNotifications(notifications);
showingLatest = false;
primaire.getWindowUpdater().remove(this);
}
}
// - -%- -
private boolean
fetchPage(String maxId, String minId)
{
display.setCursor(new Cursor(Cursor.WAIT_CURSOR));
class Handler implements RequestListener {
boolean
succeeded = false;
// -=%=-
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)
{
readEntity(json);
succeeded = true;
}
}
Handler handler = new Handler();
api.getNotifications(ROW_COUNT, maxId, minId, handler);
display.setCursor(null);
repaint();
return handler.succeeded;
}
// ---%-@-%---
NotificationsWindow(JKomasto primaire)
{
super("Notifications");
this.primaire = primaire;
this.api = primaire.getMastodonApi();
notifications = new ArrayList<>();
display = new NotificationsComponent(this);
display.setPreferredSize(new Dimension(256, 260));
setContentPane(display);
pack();
setIconImage(primaire.getProgramIcon());
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 = 10;
// ---%-@-%---
public void
showNotifications(List<Notification> notifications)
{
for (int o = 0; o < notifications.size(); ++o)
{
Notification n = notifications.get(o);
NotificationComponent c = rows.get(o);
c.setName(n.actorName);
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);
}
for (int o = notifications.size(); o < rows.size(); ++o)
{
NotificationComponent c = rows.get(o);
c.setName("");
c.setType("");
c.setText("");
}
prev.setEnabled(true);
next.setEnabled(!notifications.isEmpty());
}
// - -%- -
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 ImageComponent
typeImg;
private JLabel
name, text;
// ---%-@-%---
public void
setType(String n)
{
type.setText(n);
typeImg.image = ImageApi.local(n + "Notification");
}
public void
setName(String n) { name.setText(n); }
public void
setText(String n) { text.setText(n); }
// - -%- -
public void
doLayout()
{
int w = getWidth(), h = getHeight();
int x0 = w * 0/20;
int x1 = w * 7/20;
int x2 = w * 13/20;
int x3 = w * 15/20;
int x4 = w * 20/20;
name.setLocation(x0 + 4, 0);
name.setSize((x1 - 4) - (x0 + 4), h);
type.setLocation(x1 + 4, 0);
type.setSize((x2 - 1) - (x1 + 4), h);
typeImg.setLocation((x2 + 1), 2);
typeImg.setSize((x3 - 4) - (x2 + 1), (h - 2) - 2);
text.setLocation(x3 + 4, 0);
text.setSize((x4 - 4) - (x3 + 4), h);
}
public void
componentResized(ComponentEvent eC) { doLayout(); }
public void
componentShown(ComponentEvent eC) { }
public void
componentHidden(ComponentEvent eC) { }
public void
componentMoved(ComponentEvent eC) { }
// ---%-@-%---
private static class
ImageComponent extends JComponent {
private Image
image,
scaled;
private boolean
snapdown = false;
// -=%=-
protected void
paintComponent(Graphics g)
{
if (image == null) return;
int w = getWidth(), h = getHeight();
int ow = image.getWidth(this);
int oh = image.getHeight(this);
int nh = h;
int nw = ow * nh/oh;
if (snapdown)
{
int sw, sh;
for (sw = 1; sw < nw; sw *= 2);
for (sh = 1; sh < nh; sh *= 2);
nw = sw / 2;
nh = sh / 2;
}
if (scaled == null)
scaled = image.getScaledInstance(
nw, nh,
Image.SCALE_SMOOTH
);
int x = (w - nw) / 2;
int y = (h - nh) / 2;
g.drawImage(scaled, x, y, this);
}
}
// ---%-@-%---
NotificationComponent()
{
Font f = new Font("Dialog", Font.PLAIN, 12);
Font f1 = f.deriveFont(Font.PLAIN, 14);
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.setFont(f1);
type = new JLabel();
type.setFont(f2);
type.setHorizontalAlignment(JLabel.RIGHT);
typeImg = new ImageComponent();
text = new JLabel();
text.setFont(f3);
setLayout(null);
add(name);
add(type);
add(typeImg);
add(text);
this.addComponentListener(this);
setBorder(b1);
}
}