├── .gitignore
├── banner1.png
├── src
└── main
│ ├── resources
│ ├── css
│ │ └── tabpane.css
│ └── fxml
│ │ ├── RepoItem.fxml
│ │ ├── FollowerItem.fxml
│ │ ├── FXMLLogin.fxml
│ │ └── FXMLHome.fxml
│ └── java
│ ├── utilities
│ ├── Constants.java
│ └── Utilities.java
│ └── app
│ ├── Main.java
│ ├── controllers
│ ├── FollowerItemController.java
│ ├── RepoItemController.java
│ └── HomeController.java
│ └── LoginController.java
├── nb-configuration.xml
├── README.md
├── nbactions.xml
└── pom.xml
/.gitignore:
--------------------------------------------------------------------------------
1 | /target/
--------------------------------------------------------------------------------
/banner1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DevTony101/JFXGithubClient/HEAD/banner1.png
--------------------------------------------------------------------------------
/src/main/resources/css/tabpane.css:
--------------------------------------------------------------------------------
1 | .jfx-tab-pane .headers-region {
2 | -fx-background-color: black;
3 | }
4 |
5 | .jfx-tab-pane .tab-header-background {
6 | -fx-background-color: black;
7 | }
8 |
9 | .jfx-tab-pane .control-buttons-tab .jfx-rippler {
10 | -jfx-rippler-fill: white;
11 | }
--------------------------------------------------------------------------------
/src/main/java/utilities/Constants.java:
--------------------------------------------------------------------------------
1 | package utilities;
2 |
3 | // Utility constants
4 | public class Constants {
5 |
6 | public static final String APP_TITLE = "JFX Github Client";
7 | public static final String FXML_LOGIN = "/fxml/FXMLLogin.fxml";
8 | public static final String FXML_HOME = "/fxml/FXMLHome.fxml";
9 | public static final String FXML_REPO_ITEM = "/fxml/RepoItem.fxml";
10 | public static final String FXML_FOLLOWER_ITEM = "/fxml/FollowerItem.fxml";
11 | public static final String GITHUB_JOIN_URL = "https://github.com/join";
12 | }
13 |
--------------------------------------------------------------------------------
/nb-configuration.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
10 |
16 | none
17 |
18 |
19 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # JFXGithubClient
2 | A JavaFX Github Client (For Demonstration Purposes Only).
3 |
4 | 
5 |
6 | The project uses the [Github API for Java](https://github.com/github-api/github-api). You can use it as soon as you download the project.
7 |
8 | ## Feautures
9 | The project currently supports the following operations:
10 | - Log in to your GitHub user account
11 | - List all your followers
12 | - List and search through all your repositories (Private and Public)
13 | - Delete a repository (Be careful!)
14 |
15 | ## Future Improvements
16 | - As shown in the [documentation](http://github-api.kohsuke.org/), is a bad idea to authenticate a user with its password directly, so a future version of the project might try with the Personal Access Token.
17 | - To show the file directory of the project.
18 | - Implement the rest of the GitHub functions.
19 |
20 | ## Inspiration
21 | The app's design is based on GitHub's actual page. Thanks to the guys at [github-api](https://github.com/github-api) for their well documented API.
22 |
--------------------------------------------------------------------------------
/nbactions.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | run
5 |
6 | clean
7 | package
8 | org.codehaus.mojo:exec-maven-plugin:1.2.1:exec
9 |
10 |
11 | -jar "${project.build.directory}/${project.build.finalName}.jar"
12 |
13 |
14 |
15 | debug
16 |
17 | clean
18 | package
19 | org.codehaus.mojo:exec-maven-plugin:1.2.1:exec
20 |
21 |
22 | -Xdebug -Xrunjdwp:transport=dt_socket,server=n,address=${jpda.address} -Dglass.disableGrab=true -jar "${project.build.directory}/${project.build.finalName}.jar"
23 | true
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/src/main/resources/fxml/RepoItem.fxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/src/main/resources/fxml/FollowerItem.fxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
23 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/src/main/java/app/Main.java:
--------------------------------------------------------------------------------
1 | package app;
2 |
3 | import javafx.application.Application;
4 | import javafx.fxml.FXMLLoader;
5 | import javafx.scene.Parent;
6 | import javafx.scene.Scene;
7 | import javafx.scene.paint.Color;
8 | import javafx.stage.Stage;
9 | import javafx.stage.StageStyle;
10 | import utilities.Constants;
11 |
12 | import java.net.URL;
13 |
14 | public class Main extends Application {
15 |
16 | private double xPos, yPos;
17 |
18 | @Override
19 | public void start(Stage primaryStage) throws Exception {
20 | URL path = getClass().getResource(Constants.FXML_LOGIN);
21 | if (path != null) {
22 | Parent root = FXMLLoader.load(path);
23 | root.setOnMousePressed((value) -> {
24 | xPos = value.getSceneX();
25 | yPos = value.getSceneY();
26 | });
27 |
28 | root.setOnMouseDragged((value) -> {
29 | primaryStage.setX(value.getScreenX() - xPos);
30 | primaryStage.setY(value.getScreenY() - yPos);
31 | });
32 |
33 | Scene scene = new Scene(root);
34 | scene.setFill(Color.TRANSPARENT);
35 |
36 | primaryStage.setTitle(Constants.APP_TITLE);
37 | primaryStage.initStyle(StageStyle.TRANSPARENT);
38 | primaryStage.setScene(scene);
39 | primaryStage.show();
40 | } else {
41 | System.err.println("Main FXML Could Not Be Loaded.");
42 | System.exit(-1);
43 | }
44 | }
45 |
46 | public static void main(String[] args) {
47 | launch(args);
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/main/java/utilities/Utilities.java:
--------------------------------------------------------------------------------
1 | /*
2 | * To change this license header, choose License Headers in Project Properties.
3 | * To change this template file, choose Tools | Templates
4 | * and open the template in the editor.
5 | */
6 | package utilities;
7 |
8 | import java.io.PrintWriter;
9 | import java.io.StringWriter;
10 | import javafx.scene.control.Alert;
11 | import javafx.scene.control.Label;
12 | import javafx.scene.control.TextArea;
13 | import javafx.scene.layout.GridPane;
14 | import javafx.scene.layout.Priority;
15 |
16 | /**
17 | *
18 | * @author Tony Manjarres
19 | */
20 | public class Utilities {
21 |
22 | public static void showException(String header, Exception e) {
23 | Alert alert = new Alert(Alert.AlertType.ERROR);
24 | alert.setTitle("Oops! An error ocurred");
25 | alert.setHeaderText(header);
26 |
27 | StringWriter sw = new StringWriter();
28 | PrintWriter pw = new PrintWriter(sw);
29 | e.printStackTrace(pw);
30 | String exceptionText = sw.toString();
31 | Label label = new Label("Here is the traceback: ");
32 |
33 | TextArea textArea = new TextArea(exceptionText);
34 | textArea.setEditable(false);
35 | textArea.setWrapText(true);
36 | textArea.setMaxWidth(Double.MAX_VALUE);
37 | textArea.setMaxHeight(Double.MAX_VALUE);
38 | GridPane.setVgrow(textArea, Priority.ALWAYS);
39 | GridPane.setHgrow(textArea, Priority.ALWAYS);
40 |
41 | GridPane expContent = new GridPane();
42 | expContent.setMaxWidth(Double.MAX_VALUE);
43 | expContent.add(label, 0, 0);
44 | expContent.add(textArea, 0, 1);
45 |
46 | alert.getDialogPane().setExpandableContent(expContent);
47 | alert.showAndWait();
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/main/java/app/controllers/FollowerItemController.java:
--------------------------------------------------------------------------------
1 | package app.controllers;
2 |
3 | /*
4 | * To change this license header, choose License Headers in Project Properties.
5 | * To change this template file, choose Tools | Templates
6 | * and open the template in the editor.
7 | */
8 |
9 | import java.io.IOException;
10 | import java.net.URL;
11 | import java.util.ResourceBundle;
12 | import java.util.logging.Level;
13 | import java.util.logging.Logger;
14 | import javafx.fxml.FXML;
15 | import javafx.fxml.Initializable;
16 | import javafx.scene.control.Button;
17 | import javafx.scene.control.Label;
18 | import javafx.scene.image.Image;
19 | import javafx.scene.image.ImageView;
20 | import javafx.scene.input.MouseEvent;
21 | import org.kohsuke.github.GHUser;
22 |
23 | import utilities.*;
24 |
25 | /**
26 | * FXML Controller class
27 | *
28 | * @author Tony Manjarres
29 | */
30 | public class FollowerItemController implements Initializable {
31 |
32 | @FXML
33 | private ImageView imAvatar;
34 |
35 | @FXML
36 | private Label lblName, lblUsername;
37 |
38 | @FXML
39 | private Button btnFollow;
40 |
41 | /**
42 | * Called to initialize a controller after its root element has been
43 | * completely processed.
44 | *
45 | * @param location The location used to resolve relative paths for the root
46 | * object, or
47 | * null if the location is not known.
48 | * @param resources The resources used to localize the root object, or
49 | * null if
50 | */
51 | @Override
52 | public void initialize(URL location, ResourceBundle resources) {
53 | // TODO
54 | }
55 |
56 | public void setFollower(GHUser user, GHUser follower) {
57 | try {
58 | String name = (follower.getName() != null && !follower.getName().isEmpty() ? follower.getName() : "No Name Available");
59 | lblName.setText(name);
60 | lblUsername.setText("@" + follower.getLogin());
61 | Image image = new Image(follower.getAvatarUrl());
62 | imAvatar.setImage(image);
63 | btnFollow.setVisible(!user.getFollows().contains(follower));
64 | } catch (IOException ex) {
65 | Utilities.showException("Could not load followers.", ex);
66 | Logger.getLogger(FollowerItemController.class.getName()).log(Level.SEVERE, null, ex);
67 | }
68 | }
69 |
70 | @FXML
71 | void followUser(MouseEvent event) {
72 | // TODO: Implement this functionality
73 | }
74 |
75 | }
76 |
--------------------------------------------------------------------------------
/src/main/java/app/controllers/RepoItemController.java:
--------------------------------------------------------------------------------
1 | /*
2 | * To change this license header, choose License Headers in Project Properties.
3 | * To change this template file, choose Tools | Templates
4 | * and open the template in the editor.
5 | */
6 | package app.controllers;
7 |
8 | import de.jensd.fx.glyphs.octicons.OctIcon;
9 | import de.jensd.fx.glyphs.octicons.OctIconView;
10 | import java.net.URL;
11 | import java.util.ResourceBundle;
12 | import javafx.event.ActionEvent;
13 | import javafx.fxml.FXML;
14 | import javafx.fxml.Initializable;
15 | import javafx.scene.control.Label;
16 | import javafx.scene.paint.Paint;
17 | import org.kohsuke.github.GHRepository;
18 |
19 | /**
20 | * FXML Controller class
21 | *
22 | * @author Tony Manjarres
23 | */
24 | public class RepoItemController implements Initializable {
25 |
26 | @FXML
27 | private Label lblRepoName, lblRepoDesc, lblFork;
28 |
29 | @FXML
30 | private OctIconView icon;
31 |
32 | //
33 | private GHRepository repo;
34 |
35 | /**
36 | * Called to initialize a controller after its root element has been
37 | * completely processed.
38 | *
39 | * @param location The location used to resolve relative paths for the root
40 | * object, or
41 | * null if the location is not known.
42 | * @param resources The resources used to localize the root object, or
43 | * null if
44 | */
45 | @Override
46 | public void initialize(URL location, ResourceBundle resources) {
47 | // TODO
48 | }
49 |
50 | public void setRepository(GHRepository repo) {
51 | this.repo = repo;
52 | if (repo.isFork()) {
53 | icon.setIcon(OctIcon.REPO_FORKED);
54 | icon.setFill(Paint.valueOf("#6A737D"));
55 | lblFork.setText("Forked");
56 | } else if (repo.isPrivate()) {
57 | icon.setIcon(OctIcon.LOCK);
58 | icon.setFill(Paint.valueOf("#DBAB09"));
59 | lblFork.setText("Private");
60 | } else {
61 | icon.setIcon(OctIcon.REPO);
62 | icon.setFill(Paint.valueOf("#6A737D"));
63 | lblFork.setText("Public");
64 | }
65 |
66 | lblRepoName.setText(repo.getName());
67 | lblRepoDesc.setText("No Description Available.");
68 | String desc = repo.getDescription();
69 | if (desc != null && !desc.isEmpty()) {
70 | lblRepoDesc.setText(desc);
71 | }
72 | }
73 |
74 | @FXML
75 | void setSelectedRepository(ActionEvent event) {
76 | HomeController.SELECTED = this.repo;
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/src/main/resources/fxml/FXMLLogin.fxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
52 |
53 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
72 |
73 |
78 |
79 |
80 |
81 |
82 |
--------------------------------------------------------------------------------
/src/main/java/app/LoginController.java:
--------------------------------------------------------------------------------
1 | package app;
2 |
3 | import com.jfoenix.controls.JFXPasswordField;
4 | import com.jfoenix.controls.JFXTextField;
5 | import java.awt.Desktop;
6 | import java.io.IOException;
7 | import java.net.URI;
8 | import javafx.fxml.Initializable;
9 |
10 | import java.net.URL;
11 | import java.util.ResourceBundle;
12 | import javafx.event.ActionEvent;
13 | import javafx.fxml.FXML;
14 | import javafx.fxml.FXMLLoader;
15 | import javafx.geometry.Pos;
16 | import javafx.scene.Node;
17 | import javafx.scene.Parent;
18 | import javafx.scene.Scene;
19 | import javafx.scene.input.MouseEvent;
20 | import javafx.scene.paint.Color;
21 | import javafx.stage.Stage;
22 | import javafx.util.Duration;
23 | import org.controlsfx.control.Notifications;
24 | import org.kohsuke.github.GHUser;
25 | import org.kohsuke.github.GitHub;
26 | import org.kohsuke.github.GitHubBuilder;
27 |
28 | import utilities.*;
29 |
30 | /**
31 | * FXML Controller class
32 | *
33 | * @author Tony Manjarres
34 | */
35 | public class LoginController implements Initializable {
36 |
37 | @FXML
38 | private JFXTextField usernameField;
39 |
40 | @FXML
41 | private JFXPasswordField passwordField;
42 |
43 | //
44 | public static GHUser GH_USER = null;
45 | private double xPos, yPos;
46 |
47 | @FXML
48 | private void closeWindow(MouseEvent event) {
49 | System.exit(0);
50 | }
51 |
52 | @FXML
53 | private void joinGithub(ActionEvent event) {
54 | openWebBrowser(Constants.GITHUB_JOIN_URL);
55 | }
56 |
57 | @FXML
58 | private void signIn(MouseEvent event) {
59 | String username = usernameField.getText().trim();
60 | String password = passwordField.getText().trim();
61 | if (username.isEmpty() && password.isEmpty()) {
62 | Notifications nf = makeNotification("Error!", "Fields Can't Be Blank!");
63 | nf.showError();
64 | } else {
65 | try {
66 | GitHub github = new GitHubBuilder().withPassword(username, password).build();
67 | GH_USER = github.getMyself();
68 |
69 | Node node = (Node) event.getSource();
70 | Stage stage = (Stage) node.getScene().getWindow();
71 | stage.close();
72 |
73 | URL path = getClass().getResource(Constants.FXML_HOME);
74 | if (path != null) {
75 | Parent root = FXMLLoader.load(path);
76 | root.setOnMousePressed((value) -> {
77 | xPos = value.getSceneX();
78 | yPos = value.getSceneY();
79 | });
80 |
81 | root.setOnMouseDragged((value) -> {
82 | stage.setX(value.getScreenX() - xPos);
83 | stage.setY(value.getScreenY() - yPos);
84 | });
85 |
86 | Scene scene = new Scene(root);
87 | scene.setFill(Color.TRANSPARENT);
88 |
89 | stage.setTitle(Constants.APP_TITLE);
90 | stage.setScene(scene);
91 | stage.show();
92 | } else {
93 | throw new IOException("Error Loading Home FXML File.");
94 | }
95 | } catch (IOException e) {
96 | System.err.println("Error With Login: " + e.getMessage());
97 | Notifications nf = makeNotification("Error!", "Invalid User or Password.");
98 | nf.showError();
99 | }
100 | }
101 | }
102 |
103 | /**
104 | * Called to initialize a controller after its root element has been
105 | * completely processed.
106 | *
107 | * @param location The location used to resolve relative paths for the root
108 | * object, or
109 | * null if the location is not known.
110 | * @param resources The resources used to localize the root object, or
111 | * null if
112 | */
113 | @Override
114 | public void initialize(URL location, ResourceBundle resources) {
115 |
116 | }
117 |
118 | private void openWebBrowser(String uri) {
119 | try {
120 | Desktop.getDesktop().browse(URI.create(uri));
121 | } catch (IOException e) {
122 | System.err.println("Error Open Web Browser: " + e.getMessage());
123 | String title = "Oh No! An Error Has Occurred!";
124 | String text = Constants.APP_TITLE + " is unable to open a web browser.";
125 | Notifications nf = makeNotification(title, text);
126 | nf.showError();
127 | }
128 | }
129 |
130 | private Notifications makeNotification(String title, String text) {
131 | Notifications builder = Notifications.create()
132 | .title(title)
133 | .text(text)
134 | .graphic(null)
135 | .hideAfter(Duration.seconds(3))
136 | .position(Pos.BOTTOM_RIGHT);
137 | return builder;
138 | }
139 |
140 | }
141 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 |
5 | com.appplus
6 | JFXGithubClient
7 | 1.0-SNAPSHOT
8 | jar
9 |
10 | JFXGithubClient
11 |
12 |
13 | UTF-8
14 | app.Main
15 |
16 |
17 |
18 |
19 | Your Organisation
20 |
21 |
22 |
23 |
24 |
25 | org.apache.maven.plugins
26 | maven-dependency-plugin
27 | 2.6
28 |
29 |
30 | unpack-dependencies
31 | package
32 |
33 | unpack-dependencies
34 |
35 |
36 | system
37 | junit,org.mockito,org.hamcrest
38 | ${project.build.directory}/classes
39 |
40 |
41 |
42 |
43 |
44 | org.codehaus.mojo
45 | exec-maven-plugin
46 | 1.2.1
47 |
48 |
49 | unpack-dependencies
50 |
51 | package
52 |
53 | exec
54 |
55 |
56 | ${java.home}/../bin/javafxpackager
57 |
58 | -createjar
59 | -nocss2bin
60 | -appclass
61 | ${mainClass}
62 | -srcdir
63 | ${project.build.directory}/classes
64 | -outdir
65 | ${project.build.directory}
66 | -outfile
67 | ${project.build.finalName}.jar
68 |
69 |
70 |
71 |
72 | default-cli
73 |
74 | exec
75 |
76 |
77 | ${java.home}/bin/java
78 | ${runfx.args}
79 |
80 |
81 |
82 |
83 |
84 | org.apache.maven.plugins
85 | maven-compiler-plugin
86 | 3.1
87 |
88 | 1.8
89 | 1.8
90 |
91 | ${sun.boot.class.path}${path.separator}${java.home}/lib/jfxrt.jar
92 |
93 |
94 |
95 |
96 | org.apache.maven.plugins
97 | maven-surefire-plugin
98 | 2.16
99 |
100 |
101 | ${java.home}/lib/jfxrt.jar
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 | com.jfoenix
111 | jfoenix
112 | 8.0.8
113 |
114 |
115 | org.controlsfx
116 | controlsfx
117 | 8.40.15
118 |
119 |
120 | de.jensd
121 | fontawesomefx
122 | 8.9
123 |
124 |
125 |
126 |
127 | org.kohsuke
128 | github-api
129 | 1.101
130 |
131 |
132 |
133 |
134 |
--------------------------------------------------------------------------------
/src/main/java/app/controllers/HomeController.java:
--------------------------------------------------------------------------------
1 | package app.controllers;
2 |
3 | import app.LoginController;
4 | import com.jfoenix.controls.JFXButton;
5 | import com.jfoenix.controls.JFXSpinner;
6 | import java.awt.Desktop;
7 | import java.io.IOException;
8 | import java.net.URI;
9 | import java.net.URL;
10 | import java.util.HashMap;
11 | import java.util.Map;
12 | import java.util.Optional;
13 | import java.util.ResourceBundle;
14 | import java.util.logging.Level;
15 | import java.util.logging.Logger;
16 | import javafx.animation.KeyFrame;
17 | import javafx.animation.Timeline;
18 | import javafx.concurrent.Task;
19 | import javafx.fxml.FXML;
20 | import javafx.fxml.FXMLLoader;
21 | import javafx.fxml.Initializable;
22 | import javafx.scene.Node;
23 | import javafx.scene.Parent;
24 | import javafx.scene.Scene;
25 | import javafx.scene.control.Alert;
26 | import javafx.scene.control.Alert.AlertType;
27 | import javafx.scene.control.ButtonType;
28 | import javafx.scene.control.Label;
29 | import javafx.scene.control.TextField;
30 | import javafx.scene.image.Image;
31 | import javafx.scene.image.ImageView;
32 | import javafx.scene.input.KeyEvent;
33 | import javafx.scene.input.MouseEvent;
34 | import javafx.scene.layout.Pane;
35 | import javafx.scene.layout.VBox;
36 | import javafx.scene.paint.Color;
37 | import javafx.stage.Stage;
38 | import javafx.util.Duration;
39 | import org.kohsuke.github.GHPerson;
40 | import org.kohsuke.github.GHRepository;
41 | import org.kohsuke.github.GHUser;
42 | import utilities.*;
43 |
44 | /**
45 | * FXML Controller class
46 | *
47 | * @author Tony Manjarres
48 | */
49 | public class HomeController implements Initializable {
50 |
51 | @FXML
52 | private Label lblName, lblUsername;
53 |
54 | @FXML
55 | private ImageView imAvatar;
56 |
57 | @FXML
58 | private Pane avatarPane;
59 |
60 | @FXML
61 | private VBox vbRepos, vbFollowers;
62 |
63 | @FXML
64 | private Label lblRepoName, lblPrivate, lblRepoLanguage;
65 |
66 | @FXML
67 | private Label lblRepoDesc, lblRepoCreation, lblRepoParent, lblBranches;
68 |
69 | @FXML
70 | private JFXButton btnDeleteRepo, btnBrowser;
71 |
72 | @FXML
73 | private TextField searchField;
74 |
75 | //
76 | private double xPos, yPos;
77 | private GHRepository cache = null;
78 | public static GHRepository SELECTED = null;
79 | public Map repos;
80 |
81 | @FXML
82 | private void closeWindow(MouseEvent event) {
83 | System.exit(0);
84 | }
85 |
86 | @FXML
87 | private void hideWindow(MouseEvent event) {
88 | Node node = (Node) event.getSource();
89 | Stage stage = (Stage) node.getScene().getWindow();
90 | stage.setIconified(true);
91 | }
92 |
93 | @FXML
94 | private void logOut(MouseEvent event) {
95 | try {
96 | Node node = (Node) event.getSource();
97 | Stage stage = (Stage) node.getScene().getWindow();
98 | stage.close();
99 | URL path = getClass().getResource(Constants.FXML_LOGIN);
100 | if (path != null) {
101 | Parent root = FXMLLoader.load(path);
102 | root.setOnMousePressed((value) -> {
103 | xPos = value.getSceneX();
104 | yPos = value.getSceneY();
105 | });
106 |
107 | root.setOnMouseDragged((value) -> {
108 | stage.setX(value.getScreenX() - xPos);
109 | stage.setY(value.getScreenY() - yPos);
110 | });
111 |
112 | LoginController.GH_USER = null;
113 | Scene scene = new Scene(root);
114 | scene.setFill(Color.TRANSPARENT);
115 |
116 | stage.setTitle(Constants.APP_TITLE);
117 | stage.setScene(scene);
118 | stage.show();
119 | } else {
120 | throw new IOException("Error Loading Login FXML File.");
121 | }
122 | } catch (IOException e) {
123 | Utilities.showException("There was an error trying to log out.", e);
124 | Logger.getLogger(HomeController.class.getName()).log(Level.SEVERE, null, e);
125 | }
126 | }
127 |
128 | @FXML
129 | private void deleteRepo(MouseEvent event) {
130 | Alert alert = new Alert(AlertType.CONFIRMATION);
131 | alert.setTitle("Repository Deletion Confirmation");
132 | alert.setContentText("Are you sure you want to delete this repository?");
133 | Optional result = alert.showAndWait();
134 | if (result.get() == ButtonType.OK) {
135 | GHRepository selected = this.cache;
136 | if (selected != null) try {
137 | selected.delete();
138 | clearDetails();
139 | this.repos = null;
140 | loadRepositories(LoginController.GH_USER, null);
141 | } catch (IOException ex) {
142 | Utilities.showException("Could not delete the selected repo.", ex);
143 | Logger.getLogger(HomeController.class.getName()).log(Level.SEVERE, null, ex);
144 | }
145 | }
146 | }
147 |
148 | @FXML
149 | void openRepo(MouseEvent event) {
150 | GHPerson user = LoginController.GH_USER;
151 | String uri = "https://github.com/" + user.getLogin() + "/" + this.cache.getName();
152 | try {
153 | Desktop.getDesktop().browse(URI.create(uri));
154 | } catch (IOException e) {
155 | Utilities.showException("Could not open the selected repository.", e);
156 | Logger.getLogger(HomeController.class.getName()).log(Level.SEVERE, null, e);
157 | }
158 | }
159 |
160 | @FXML
161 | void reloadRepositories(KeyEvent event) {
162 | String search = searchField.getText().trim().toLowerCase();
163 | try {
164 | loadRepositories(null, search);
165 | } catch (IOException ex) {
166 | Logger.getLogger(HomeController.class.getName()).log(Level.SEVERE, null, ex);
167 | }
168 | }
169 |
170 | @FXML
171 | void createNewRepository(MouseEvent event) {
172 | // TODO: Implement this
173 | }
174 |
175 | /**
176 | * Called to initialize a controller after its root element has been
177 | * completely processed.
178 | *
179 | * @param location The location used to resolve relative paths for the root
180 | * object, or
181 | * null if the location is not known.
182 | * @param resources The resources used to localize the root object, or
183 | * null if
184 | */
185 | @Override
186 | public void initialize(URL location, ResourceBundle resources) {
187 | // TODO
188 | clearDetails();
189 | GHPerson user = LoginController.GH_USER;
190 | try {
191 | String name = (user.getName() != null && !user.getName().isEmpty() ? user.getName() : "No Name Available");
192 | lblName.setText(name);
193 | lblUsername.setText("@" + user.getLogin());
194 | Image image = new Image(user.getAvatarUrl());
195 | imAvatar.setImage(image);
196 | avatarPane.setMinWidth(image.getRequestedWidth());
197 | avatarPane.setMaxWidth(image.getRequestedWidth());
198 | loadRepositories(user, null);
199 | loadFollowers((GHUser) user);
200 | } catch (IOException ex) {
201 | Logger.getLogger(HomeController.class.getName()).log(Level.SEVERE, null, ex);
202 | Utilities.showException("Could not load user info.", ex);
203 | }
204 | initHandler();
205 | }
206 |
207 | private void initHandler() {
208 | // TODO: Implement the observer pattern to avoid using this approach
209 | Timeline timeline = new Timeline(new KeyFrame(Duration.millis(100), (event) -> {
210 | GHRepository selected = SELECTED;
211 | if (selected != null) try {
212 | this.cache = SELECTED;
213 | btnDeleteRepo.setDisable(false);
214 | btnBrowser.setDisable(false);
215 | lblRepoName.setText(selected.getName());
216 | lblRepoLanguage.setText(selected.getLanguage());
217 | lblBranches.setText(String.valueOf(selected.getBranches().size()));
218 | String desc = selected.getDescription();
219 | lblRepoDesc.setText("No Description Available.");
220 | if (desc != null && !desc.isEmpty()) {
221 | lblRepoDesc.setText(desc);
222 | }
223 | lblPrivate.setText(selected.isPrivate() ? "Private" : "Public");
224 | lblRepoCreation.setText(selected.getCreatedAt().toString().substring(0, 10));
225 | lblRepoParent.setText("Does Not Have");
226 | if (selected.isFork()) {
227 | GHRepository parent = selected.getParent();
228 | lblRepoParent.setText(parent.getOwnerName());
229 | }
230 | } catch (IOException e) {
231 | Logger.getLogger(HomeController.class.getName()).log(Level.SEVERE, null, e);
232 | Utilities.showException("Error Rendering Repo Details.", e);
233 | } finally {
234 | SELECTED = null;
235 | }
236 | }));
237 | timeline.setCycleCount(Timeline.INDEFINITE);
238 | timeline.play();
239 | }
240 |
241 | private void loadRepositories(GHPerson user, String filter) throws IOException {
242 | if (this.repos == null) {
243 | this.repos = new HashMap<>(user.getRepositories());
244 | }
245 | vbRepos.getChildren().clear();
246 | for (String key : this.repos.keySet()) {
247 | GHRepository repo = this.repos.get(key);
248 | if (repo != null) {
249 | if (filter != null) {
250 | String name = repo.getName().toLowerCase();
251 | if (!name.contains(filter)) {
252 | continue;
253 | }
254 | }
255 | FXMLLoader loader = new FXMLLoader(getClass().getResource(Constants.FXML_REPO_ITEM));
256 | RepoItemController controller = new RepoItemController();
257 | loader.setController(controller);
258 | vbRepos.getChildren().add(loader.load());
259 | controller.setRepository(repo);
260 | }
261 | }
262 | }
263 |
264 | private void loadFollowers(GHUser user) throws IOException {
265 | vbFollowers.getChildren().clear();
266 | for (GHUser follower : user.getFollowers()) {
267 | FXMLLoader loader = new FXMLLoader(getClass().getResource(Constants.FXML_FOLLOWER_ITEM));
268 | FollowerItemController controller = new FollowerItemController();
269 | loader.setController(controller);
270 | vbFollowers.getChildren().add(loader.load());
271 | controller.setFollower(user, follower);
272 | }
273 | }
274 |
275 | private void clearDetails() {
276 | lblRepoName.setText("");
277 | lblRepoDesc.setText("");
278 | lblRepoCreation.setText("");
279 | lblRepoLanguage.setText("");
280 | lblRepoParent.setText("");
281 | lblBranches.setText("");
282 | btnDeleteRepo.setDisable(true);
283 | btnBrowser.setDisable(true);
284 | SELECTED = null;
285 | this.cache = null;
286 | }
287 |
288 | }
289 |
--------------------------------------------------------------------------------
/src/main/resources/fxml/FXMLHome.fxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
48 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
89 |
90 |
95 |
100 |
101 |
106 |
111 |
116 |
121 |
126 |
131 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
149 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
174 |
175 |
176 |
177 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
--------------------------------------------------------------------------------