biskuteri-cafe-JKomasto2/LoginWindow.java

609 lines
17 KiB
Java
Raw Permalink Normal View History

/* 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.JButton;
import javax.swing.JLabel;
import javax.swing.JTextField;
import javax.swing.JPasswordField;
import javax.swing.JTextArea;
import javax.swing.JCheckBox;
import javax.swing.JOptionPane;
import javax.swing.BorderFactory;
import javax.swing.border.Border;
import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Cursor;
import java.awt.Desktop;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import cafe.biskuteri.hinoki.Tree;
import java.net.URI;
import java.io.IOException;
class
LoginWindow extends JFrame {
private JKomasto
primaire;
private MastodonApi
api;
// - -%- -
private LoginComponent
display;
private boolean
serverContacted = false,
haveAppCredentials = false,
haveAccessToken = false,
haveAccountDetails = false;
// ---%-@-%---
public void
updateStatusDisplay()
{
char prefix1 = serverContacted ? '✓' : '✕';
char prefix2 = haveAppCredentials ? '✓' : '✕';
char prefix3 = haveAccessToken ? '✓' : '✕';
char prefix4 = haveAccountDetails ? '✓' : '✕';
StringBuilder b = new StringBuilder();
b.append(prefix1 + " Have connection to server\n");
b.append(prefix2 + " Have app credentials\n");
b.append(prefix3 + " Have access token\n");
b.append(prefix4 + " Have account details\n");
display.setText(b.toString());
display.paintImmediately(display.getBounds(null));
}
public void
useCache()
{
display.setCursor(new Cursor(Cursor.WAIT_CURSOR));
try
{
api.loadCache();
haveAppCredentials = true;
haveAccessToken = true;
display.setInstanceUrl(api.getInstanceUrl());
updateStatusDisplay();
}
catch (IOException eIo)
{
JOptionPane.showMessageDialog(
this,
"We couldn't get login details from the cache.."
+ "\n" + eIo.getClass() + ": " + eIo.getMessage()
);
display.setAutoLoginToggled(false);
}
display.setCursor(null);
if (!haveAccessToken) return;
display.setCursor(new Cursor(Cursor.WAIT_CURSOR));
api.getAccountDetails(new RequestListener() {
public void
connectionFailed(IOException eIo)
{
JOptionPane.showMessageDialog(
LoginWindow.this,
"Tried to get account details, failed.."
+ "\n" + eIo.getMessage()
);
}
public void
requestFailed(int httpCode, Tree<String> json)
{
JOptionPane.showMessageDialog(
LoginWindow.this,
"Tried to get account details, failed.."
+ "\n" + json.get("error").value
+ "\n(HTTP error code: " + httpCode + ")"
);
}
public void
requestSucceeded(Tree<String> json)
{
api.setAccountDetails(json);
serverContacted = true;
haveAccountDetails = true;
updateStatusDisplay();
}
});
display.setCursor(null);
if (!haveAccountDetails) return;
display.setCursor(new Cursor(Cursor.WAIT_CURSOR));
try
{
api.saveToCache();
}
catch (IOException eIo)
{
JOptionPane.showMessageDialog(
this,
"We couldn't save login details into the cache.."
+ "\n" + eIo.getClass() + ": " + eIo.getMessage()
);
}
display.setCursor(null);
primaire.finishedLogin();
}
public void
useInstanceUrl()
{
if (display.isAutoLoginToggled()) { useCache(); return; }
String url = display.getInstanceUrl();
if (url.trim().isEmpty()) {
// Should we show an error dialog..?
display.setInstanceUrl("");
return;
}
if (!hasProtocol(url)) {
url = "https://" + url;
display.setInstanceUrl(url);
display.paintImmediately(display.getBounds(null));
}
serverContacted = false;
haveAppCredentials = false;
haveAccessToken = false;
haveAccountDetails = false;
display.setCursor(new Cursor(Cursor.WAIT_CURSOR));
api.testUrlConnection(url, new RequestListener() {
public void
connectionFailed(IOException eIo)
{
JOptionPane.showMessageDialog(
LoginWindow.this,
"Tried to connect to URL, failed.."
+ "\n" + eIo.getClass() + ": " + eIo.getMessage()
);
}
public void
requestFailed(int httpCode, Tree<String> response)
{
JOptionPane.showMessageDialog(
LoginWindow.this,
"Tried to connect to URL, failed.."
+ "\n" + response.get("body").value
);
}
public void
requestSucceeded(Tree<String> response)
{
serverContacted = true;
updateStatusDisplay();
}
});
display.setCursor(null);
if (!serverContacted) return;
api.setInstanceUrl(url);
display.setCursor(new Cursor(Cursor.WAIT_CURSOR));
api.getAppCredentials(new RequestListener() {
public void
connectionFailed(IOException eIo)
{
JOptionPane.showMessageDialog(
LoginWindow.this,
"Tried to get app credentials, failed.."
+ "\n" + eIo.getMessage()
);
}
public void
requestFailed(int httpCode, Tree<String> json)
{
JOptionPane.showMessageDialog(
LoginWindow.this,
"Tried to get app credentials, failed.."
+ "\n" + json.get("error").value
+ "\n(HTTP error code: " + httpCode + ")"
);
}
public void
requestSucceeded(Tree<String> json)
{
api.setAppCredentials(json);
haveAppCredentials = true;
updateStatusDisplay();
}
});
display.setCursor(null);
if (!haveAppCredentials) return;
display.setCursor(new Cursor(Cursor.WAIT_CURSOR));
URI uri = api.getAuthorisationURL();
final String MESSAGE1 =
"We will need you to login through a web browser,\n"
+ "and they will give you an authorisation code\n"
+ "that you will paste here. Sorry..!";
boolean supported =
Desktop.isDesktopSupported()
&& Desktop.getDesktop().isSupported(Desktop.Action.BROWSE);
if (supported)
{
JOptionPane.showMessageDialog(
LoginWindow.this,
MESSAGE1,
"Authorisation code renewal",
JOptionPane.INFORMATION_MESSAGE
);
try { Desktop.getDesktop().browse(uri); }
catch (IOException eIo) { supported = false; }
}
if (!supported)
{
final String MESSAGE2 =
"\nWe cannot use Desktop.browse(URI) on your\n"
+ "computer.. You'll have to open your web\n"
+ "browser yourself, and copy this URL in.";
JTextField field = new JTextField();
field.setText(uri.toString());
field.setPreferredSize(new Dimension(120, 32));
field.selectAll();
JOptionPane.showMessageDialog(
LoginWindow.this,
new Object[] { MESSAGE1, MESSAGE2, field },
"Authorisation code renewal",
JOptionPane.INFORMATION_MESSAGE
);
}
display.receiveAuthorisationCode();
display.setCursor(null);
}
public void
useAuthorisationCode()
{
String code = display.getAuthorisationCode();
display.setCursor(new Cursor(Cursor.WAIT_CURSOR));
api.getAccessToken(code, new RequestListener() {
public void
connectionFailed(IOException eIo)
{
JOptionPane.showMessageDialog(
LoginWindow.this,
"Tried to get app token, failed.."
+ "\n" + eIo.getMessage()
);
}
public void
requestFailed(int httpCode, Tree<String> json)
{
JOptionPane.showMessageDialog(
LoginWindow.this,
"Tried to get app token, failed.."
+ "\n" + json.get("error").value
+ "\n(HTTP error code: " + httpCode + ")"
);
}
public void
requestSucceeded(Tree<String> json)
{
api.setAccessToken(json);
haveAccessToken = true;
updateStatusDisplay();
}
});
display.setCursor(null);
if (!haveAccessToken) return;
display.setCursor(new Cursor(Cursor.WAIT_CURSOR));
api.getAccountDetails(new RequestListener() {
public void
connectionFailed(IOException eIo)
{
JOptionPane.showMessageDialog(
LoginWindow.this,
"Tried to get account details, failed.."
+ "\n" + eIo.getMessage()
);
}
public void
requestFailed(int httpCode, Tree<String> json)
{
JOptionPane.showMessageDialog(
LoginWindow.this,
"Tried to get account details, failed.."
+ "\n" + json.get("error").value
+ "\n(HTTP error code: " + httpCode + ")"
);
}
public void
requestSucceeded(Tree<String> json)
{
api.setAccountDetails(json);
haveAccountDetails = true;
updateStatusDisplay();
}
});
display.setCursor(null);
if (!haveAccountDetails) return;
display.setCursor(new Cursor(Cursor.WAIT_CURSOR));
try
{
api.saveToCache();
}
catch (IOException eIo)
{
JOptionPane.showMessageDialog(
this,
"We couldn't save login details into the cache.."
+ "\n" + eIo.getClass() + ": " + eIo.getMessage()
);
}
display.setCursor(null);
primaire.finishedLogin();
}
// - -%- -
public static boolean
hasProtocol(String url) { return url.matches("^.+://.*"); }
// ---%-@-%---
LoginWindow(JKomasto primaire)
{
super("JKomasto - Login");
this.primaire = primaire;
this.api = primaire.getMastodonApi();
setLocationByPlatform(true);
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
display = new LoginComponent(this);
updateStatusDisplay();
display.setPreferredSize(new Dimension(320, 280));
setContentPane(display);
pack();
setIconImage(primaire.getProgramIcon());
}
}
class
LoginComponent extends JPanel
implements ActionListener {
private LoginWindow
primaire;
// - -%- -
private JTextArea
statusDisplay;
private JTextField
instanceUrlField,
authorisationCodeField;
private JLabel
instanceUrlLabel,
authorisationCodeLabel;
private JButton
instanceUrlButton,
authorisationCodeButton;
private JPanel
labelArea,
forField,
accountsPanel;
private JCheckBox
autoLoginToggle;
// ---%-@-%---
public String
getInstanceUrl()
{
return instanceUrlField.getText();
}
public void
setInstanceUrl(String url)
{
instanceUrlField.setText(url);
}
public String
getAuthorisationCode()
{
return authorisationCodeField.getText();
}
public void
setText(String status)
{
statusDisplay.setText(status);
}
public void
setAutoLoginToggled(boolean a) { autoLoginToggle.setSelected(a); }
public boolean
isAutoLoginToggled() { return autoLoginToggle.isSelected(); }
public void
actionPerformed(ActionEvent eA)
{
if (eA.getSource() == instanceUrlButton)
primaire.useInstanceUrl();
if (eA.getSource() == authorisationCodeButton)
primaire.useAuthorisationCode();
}
public void
receiveInstanceUrl()
{
labelArea.remove(authorisationCodeLabel);
forField.remove(authorisationCodeButton);
accountsPanel.remove(authorisationCodeField);
labelArea.add(instanceUrlLabel, BorderLayout.NORTH);
forField.add(instanceUrlButton, BorderLayout.EAST);
accountsPanel.add(instanceUrlField);
revalidate();
}
public void
receiveAuthorisationCode()
{
labelArea.remove(instanceUrlLabel);
forField.remove(instanceUrlButton);
accountsPanel.remove(instanceUrlField);
labelArea.add(authorisationCodeLabel, BorderLayout.NORTH);
forField.add(authorisationCodeButton, BorderLayout.EAST);
accountsPanel.add(authorisationCodeField);
revalidate();
}
// ---%-@-%---
LoginComponent(LoginWindow primaire)
{
this.primaire = primaire;
Border b1, b2, bi, bo, be;
b1 = BorderFactory.createEtchedBorder();
b2 = BorderFactory.createEmptyBorder(8, 8, 8, 8);
bi = BorderFactory.createCompoundBorder(b1, b2);
bo = BorderFactory.createEmptyBorder(8, 8, 8, 8);
be = BorderFactory.createEmptyBorder();
Font f1, f2;
f1 = new Font("Dialog", Font.PLAIN, 12);
f2 = new Font("Dialog", Font.PLAIN, 16);
// We can't use swing.Box for layout,
// it's malfunctioning for some reason.
instanceUrlField = new JTextField();
instanceUrlField.setFont(f1);
String s1 = "Enter an instance URL!";
instanceUrlLabel = new JLabel(s1);
instanceUrlLabel.setLabelFor(instanceUrlField);
instanceUrlLabel.setFont(f1);
instanceUrlButton = new JButton("Connect");
instanceUrlButton.setFont(f1);
instanceUrlButton.setMnemonic(KeyEvent.VK_C);
instanceUrlButton.addActionListener(this);
authorisationCodeField = new JPasswordField();
authorisationCodeField.setFont(f1);
String s2 = "Paste the authorisation code!";
authorisationCodeLabel = new JLabel(s2);
authorisationCodeLabel.setLabelFor(authorisationCodeField);
authorisationCodeLabel.setFont(f1);
authorisationCodeButton = new JButton("Login");
authorisationCodeButton.setFont(f1);
authorisationCodeButton.setMnemonic(KeyEvent.VK_L);
authorisationCodeButton.addActionListener(this);
labelArea = new JPanel();
labelArea.setLayout(new BorderLayout());
forField = new JPanel();
forField.setOpaque(false);
forField.setLayout(new BorderLayout());
forField.add(labelArea, BorderLayout.WEST);
String s3 = "Automatically login if possible";
autoLoginToggle = new JCheckBox(s3);
autoLoginToggle.setMnemonic(KeyEvent.VK_A);
autoLoginToggle.setBorder(be);
autoLoginToggle.setFont(f1);
JPanel optionsPanel = new JPanel();
optionsPanel.setOpaque(false);
optionsPanel.setLayout(new FlowLayout(FlowLayout.LEFT));
optionsPanel.setBorder(bi);
optionsPanel.add(autoLoginToggle);
accountsPanel = new JPanel();
accountsPanel.setOpaque(false);
accountsPanel.setLayout(new BorderLayout(0, 4));
accountsPanel.add(instanceUrlField);
accountsPanel.add(forField, BorderLayout.SOUTH);
statusDisplay = new JTextArea();
statusDisplay.setEditable(false);
statusDisplay.setBackground(null);
statusDisplay.setBorder(bi);
statusDisplay.setFont(f2);
receiveInstanceUrl();
setLayout(new BorderLayout(0, 8));
add(accountsPanel, BorderLayout.NORTH);
add(optionsPanel, BorderLayout.CENTER);
add(statusDisplay, BorderLayout.SOUTH);
setBorder(bo);
}
}