Skip to content

Commit 47b77d2

Browse files
authored
Merge pull request #335 from totetmatt/blueskyGephi
Bluesky gephi
2 parents bb6fd33 + f78570b commit 47b77d2

7 files changed

Lines changed: 401 additions & 34 deletions

File tree

modules/BlueskyGephi/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
<groupId>fr.totetmatt</groupId>
1111
<artifactId>bluesky-gephi</artifactId>
12-
<version>0.2.2</version>
12+
<version>0.2.3</version>
1313
<packaging>nbm</packaging>
1414

1515
<name>Bluesky Gephi</name>

modules/BlueskyGephi/src/main/java/fr/totetmatt/blueskygephi/BlueskyGephi.java

Lines changed: 73 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
package fr.totetmatt.blueskygephi;
22

33
import fr.totetmatt.blueskygephi.atproto.AtClient;
4+
import fr.totetmatt.blueskygephi.atproto.AtIdentity;
5+
import fr.totetmatt.blueskygephi.atproto.AtProtoException;
46
import fr.totetmatt.blueskygephi.atproto.response.common.Identity;
57
import java.awt.Color;
8+
import java.awt.EventQueue;
69
import java.util.ArrayList;
710
import java.util.HashSet;
811
import java.util.List;
@@ -11,21 +14,24 @@
1114
import java.util.concurrent.ExecutorService;
1215
import java.util.concurrent.Executors;
1316
import java.util.function.LongConsumer;
17+
import java.util.logging.Level;
1418
import java.util.logging.Logger;
1519
import java.util.prefs.Preferences;
1620
import java.util.stream.Collectors;
1721
import java.util.stream.Stream;
22+
import javax.swing.Icon;
23+
import javax.swing.UIManager;
1824
import org.gephi.graph.api.Edge;
1925
import org.gephi.graph.api.Graph;
2026
import org.gephi.graph.api.GraphController;
2127
import org.gephi.graph.api.GraphModel;
2228
import org.gephi.graph.api.Node;
23-
import org.gephi.project.api.Project;
2429
import org.gephi.project.api.ProjectController;
30+
import org.gephi.project.api.Workspace;
2531
import org.gephi.utils.progress.Progress;
2632
import org.gephi.utils.progress.ProgressTicket;
2733
import org.gephi.utils.progress.ProgressTicketProvider;
28-
import org.openide.util.Exceptions;
34+
import org.openide.awt.NotificationDisplayer;
2935
import org.openide.util.Lookup;
3036
import org.openide.util.NbPreferences;
3137
import org.openide.util.lookup.ServiceProvider;
@@ -65,12 +71,18 @@ public class BlueskyGephi {
6571
public BlueskyGephi() {
6672
}
6773

68-
private void ensureProject() {
74+
private void ensureWorkspace() {
6975
ProjectController projectController = Lookup.getDefault().lookup(ProjectController.class);
70-
Project currentProject = projectController.getCurrentProject();
71-
if (currentProject == null) {
76+
// A workspace can only live inside a project.
77+
if (projectController.getCurrentProject() == null) {
7278
projectController.newProject();
7379
}
80+
// A project can exist with no open workspace (e.g. all were closed), and
81+
// the graph model can't be resolved until one is created and opened.
82+
if (projectController.getCurrentWorkspace() == null) {
83+
Workspace workspace = projectController.newWorkspace(projectController.getCurrentProject());
84+
projectController.openWorkspace(workspace);
85+
}
7486
}
7587

7688
public boolean connect(String host,String handle, String password) {
@@ -85,6 +97,18 @@ public boolean connect(String host,String handle, String password) {
8597
public String getHost(){
8698
return nbPref.get(NBPREF_ATPROTO_HOST,"bsky.social");
8799
}
100+
101+
/**
102+
* Determines the account's PDS host from its handle (unauthenticated), so
103+
* the user can auto-fill the host field. This is a network call and must be
104+
* run off the EDT.
105+
*
106+
* @return the resolved PDS host, e.g. {@code shaggymane.us-west.host.bsky.network}.
107+
* @throws AtProtoException if the handle can't be resolved to a PDS.
108+
*/
109+
public String resolveHostFromHandle(String handle) {
110+
return AtIdentity.resolveHost(handle);
111+
}
88112
public String getHandle() {
89113
return nbPref.get(NBPREF_BSKY_HANDLE, "");
90114
}
@@ -171,6 +195,25 @@ private Edge createEdge(Node source, Node target) {
171195
return edge;
172196
}
173197

198+
/**
199+
* Reports a failure to the user without the raw NetBeans stack-trace dialog:
200+
* the full technical detail (with stack trace) is logged for debugging, while
201+
* the user sees a concise, non-blocking notification bubble with an
202+
* actionable reason.
203+
*/
204+
private void reportError(String title, String detail, Throwable t) {
205+
logger.log(Level.WARNING, title + " - " + detail, t);
206+
EventQueue.invokeLater(() -> {
207+
Icon icon = UIManager.getIcon("OptionPane.warningIcon");
208+
if (icon == null) {
209+
icon = new javax.swing.ImageIcon(
210+
new java.awt.image.BufferedImage(16, 16, java.awt.image.BufferedImage.TYPE_INT_ARGB));
211+
}
212+
NotificationDisplayer.getDefault().notify(title, icon, detail, null,
213+
NotificationDisplayer.Priority.HIGH);
214+
});
215+
}
216+
174217
private void fetchFollowerFollowsFromActor(String actor, List<String> listInit, boolean isFollowsActive, boolean isFollowersActive, boolean isDeepSearch) {
175218
// Run on a bounded background pool to keep the Gephi UI responsive.
176219
executor.submit(new FetchTask(actor, listInit, isFollowsActive, isFollowersActive, isDeepSearch));
@@ -283,9 +326,17 @@ private void process(String actor, boolean isDeepSearch, Optional<Integer> limit
283326
}
284327
}, onWaiting);
285328
}
329+
} catch (AtProtoException e) {
330+
// Expected failure mode (auth, rate limit, bad handle, network):
331+
// report a clear reason plus the server's own response, not a stack trace.
332+
if (!cancelled) {
333+
reportError("Bluesky: could not fetch \"" + actor + "\"", e.getUserMessageWithContent(), e);
334+
}
286335
} catch (Exception e) {
287336
if (!cancelled) {
288-
Exceptions.printStackTrace(e);
337+
reportError("Bluesky: could not fetch \"" + actor + "\"",
338+
"Unexpected error: " + e.getClass().getSimpleName()
339+
+ (e.getMessage() != null ? " - " + e.getMessage() : ""), e);
289340
}
290341
}
291342
}
@@ -368,11 +419,12 @@ private void initGraphTable() {
368419
}
369420

370421
public void fetchFollowerFollowsFromActors(List<String> actors, boolean isFollowsActive, boolean isFollowersActive, boolean isBlocksActive) {
422+
// Guarantee a workspace exists before touching the graph model.
423+
ensureWorkspace();
371424
if (client == null) {
372425
logger.warning("Not connected to Bluesky. Please connect before fetching.");
373426
return;
374427
}
375-
ensureProject();
376428
graphModel = Lookup.getDefault().lookup(GraphController.class).getGraphModel();
377429
initGraphTable();
378430
actors.stream()
@@ -382,11 +434,12 @@ public void fetchFollowerFollowsFromActors(List<String> actors, boolean isFollow
382434
}
383435

384436
public void fetchFollowerFollowsFromActors(List<String> actors) {
437+
// Guarantee a workspace exists before touching the graph model.
438+
ensureWorkspace();
385439
if (client == null) {
386440
logger.warning("Not connected to Bluesky. Please connect before fetching.");
387441
return;
388442
}
389-
ensureProject();
390443
graphModel = Lookup.getDefault().lookup(GraphController.class).getGraphModel();
391444
initGraphTable();
392445

@@ -407,10 +460,18 @@ public void fetchFollowerFollowsFromActors(List<String> actors) {
407460
// Resolving a list is itself several paged network calls, so do it
408461
// off the EDT instead of blocking the UI thread.
409462
executor.submit(() -> {
410-
List<String> listActor = listIds.stream()
411-
.flatMap(this::manageList)
412-
.collect(Collectors.toList());
413-
fetchFollowerFollowsFromActor(null, listActor, getIsFollowsActive(), getIsFollowersActive(), getIsDeepSearch());
463+
try {
464+
List<String> listActor = listIds.stream()
465+
.flatMap(this::manageList)
466+
.collect(Collectors.toList());
467+
fetchFollowerFollowsFromActor(null, listActor, getIsFollowsActive(), getIsFollowersActive(), getIsDeepSearch());
468+
} catch (AtProtoException e) {
469+
reportError("Bluesky: could not resolve list", e.getUserMessageWithContent(), e);
470+
} catch (Exception e) {
471+
reportError("Bluesky: could not resolve list",
472+
"Unexpected error: " + e.getClass().getSimpleName()
473+
+ (e.getMessage() != null ? " - " + e.getMessage() : ""), e);
474+
}
414475
});
415476
}
416477
}

modules/BlueskyGephi/src/main/java/fr/totetmatt/blueskygephi/BlueskyGephiMainPanel.form

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -101,20 +101,21 @@
101101
<Component id="credentialsConnectButton" max="32767" attributes="0"/>
102102
<Group type="102" attributes="0">
103103
<Group type="103" groupAlignment="0" attributes="0">
104-
<Component id="credentialsPasswordLabel" alignment="0" min="-2" max="-2" attributes="0"/>
105104
<Component id="credentialsHandleLabel" alignment="0" min="-2" max="-2" attributes="0"/>
105+
<Component id="credentialsHostLabel" alignment="0" min="-2" max="-2" attributes="0"/>
106+
<Component id="credentialsPasswordLabel" alignment="0" min="-2" max="-2" attributes="0"/>
106107
</Group>
107108
<EmptySpace type="separate" max="-2" attributes="0"/>
108109
<Group type="103" groupAlignment="0" attributes="0">
109110
<Component id="credentialsHandleField" pref="263" max="32767" attributes="0"/>
110111
<Component id="credentialsPasswordField" max="32767" attributes="0"/>
112+
<Group type="102" alignment="0" attributes="0">
113+
<Component id="credentialsHostField" pref="263" max="32767" attributes="0"/>
114+
<EmptySpace max="-2" attributes="0"/>
115+
<Component id="credentialsCheckButton" min="-2" max="-2" attributes="0"/>
116+
</Group>
111117
</Group>
112118
</Group>
113-
<Group type="102" alignment="0" attributes="0">
114-
<Component id="credentialsHostLabel" min="-2" max="-2" attributes="0"/>
115-
<EmptySpace min="-2" pref="43" max="-2" attributes="0"/>
116-
<Component id="credentialsHostField" pref="263" max="32767" attributes="0"/>
117-
</Group>
118119
</Group>
119120
<EmptySpace max="-2" attributes="0"/>
120121
</Group>
@@ -125,13 +126,14 @@
125126
<Group type="102" alignment="1" attributes="0">
126127
<EmptySpace min="0" pref="9" max="32767" attributes="0"/>
127128
<Group type="103" groupAlignment="3" attributes="0">
128-
<Component id="credentialsHostLabel" alignment="3" min="-2" max="-2" attributes="0"/>
129-
<Component id="credentialsHostField" alignment="3" min="-2" max="-2" attributes="0"/>
129+
<Component id="credentialsHandleLabel" alignment="3" min="-2" max="-2" attributes="0"/>
130+
<Component id="credentialsHandleField" alignment="3" min="-2" max="-2" attributes="0"/>
130131
</Group>
131132
<EmptySpace type="unrelated" max="-2" attributes="0"/>
132133
<Group type="103" groupAlignment="3" attributes="0">
133-
<Component id="credentialsHandleLabel" alignment="3" min="-2" max="-2" attributes="0"/>
134-
<Component id="credentialsHandleField" alignment="3" min="-2" max="-2" attributes="0"/>
134+
<Component id="credentialsHostLabel" alignment="3" min="-2" max="-2" attributes="0"/>
135+
<Component id="credentialsHostField" alignment="3" min="-2" max="-2" attributes="0"/>
136+
<Component id="credentialsCheckButton" alignment="3" min="-2" max="-2" attributes="0"/>
135137
</Group>
136138
<EmptySpace type="unrelated" max="-2" attributes="0"/>
137139
<Group type="103" groupAlignment="3" attributes="0">
@@ -209,6 +211,19 @@
209211
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="credentialsConnectButtonActionPerformed"/>
210212
</Events>
211213
</Component>
214+
<Component class="javax.swing.JButton" name="credentialsCheckButton">
215+
<Properties>
216+
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
217+
<ResourceString bundle="fr/totetmatt/blueskygephi/Bundle.properties" key="BlueskyGephiMainPanel.credentialsCheckButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
218+
</Property>
219+
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
220+
<ResourceString bundle="fr/totetmatt/blueskygephi/Bundle.properties" key="BlueskyGephiMainPanel.credentialsCheckButton.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
221+
</Property>
222+
</Properties>
223+
<Events>
224+
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="credentialsCheckButtonActionPerformed"/>
225+
</Events>
226+
</Component>
212227
</SubComponents>
213228
</Container>
214229
<Container class="javax.swing.JScrollPane" name="jScrollPane1">

0 commit comments

Comments
 (0)