mirror of https://github.com/zlatinb/muwire
wip on profile viewer and fetching full profiles
parent
fec578efa4
commit
388cad0b5d
|
@ -10,6 +10,9 @@ import com.muwire.core.messenger.UIFolderCreateEvent
|
|||
import com.muwire.core.messenger.UIFolderDeleteEvent
|
||||
import com.muwire.core.messenger.UIMessageMovedEvent
|
||||
import com.muwire.core.profile.MWProfile
|
||||
import com.muwire.core.profile.MWProfileFetcher
|
||||
import com.muwire.core.profile.MWProfileHeader
|
||||
import com.muwire.core.profile.UIProfileFetchEvent
|
||||
import com.muwire.core.update.AutoUpdater
|
||||
|
||||
import java.nio.charset.StandardCharsets
|
||||
|
@ -290,6 +293,9 @@ public class Core {
|
|||
} else
|
||||
log.info("no profile exists for ${me.getHumanReadableName()}")
|
||||
|
||||
Supplier<MWProfile> profileSupplier = this::getMyProfile
|
||||
Supplier<MWProfileHeader> profileHeaderSupplier = {getMyProfile()?.getHeader()} as Supplier
|
||||
|
||||
eventBus = new EventBus()
|
||||
|
||||
log.info("initializing i2p connector")
|
||||
|
@ -430,7 +436,7 @@ public class Core {
|
|||
eventBus.register(UIFeedUpdateEvent.class, feedClient)
|
||||
|
||||
log.info "initializing results sender"
|
||||
ResultsSender resultsSender = new ResultsSender(eventBus, i2pConnector, me, { getMyProfile()?.getHeader() } as Supplier,
|
||||
ResultsSender resultsSender = new ResultsSender(eventBus, i2pConnector, me, profileHeaderSupplier,
|
||||
props, certificateManager, chatServer, collectionManager)
|
||||
|
||||
log.info "initializing search manager"
|
||||
|
@ -480,7 +486,7 @@ public class Core {
|
|||
I2PAcceptor i2pAcceptor = new I2PAcceptor(i2pConnector::getSocketManager)
|
||||
eventBus.register(RouterConnectedEvent.class, i2pAcceptor)
|
||||
eventBus.register(RouterDisconnectedEvent.class, i2pAcceptor)
|
||||
connectionAcceptor = new ConnectionAcceptor(eventBus, me, connectionManager, props,
|
||||
connectionAcceptor = new ConnectionAcceptor(eventBus, me, profileSupplier, connectionManager, props,
|
||||
i2pAcceptor, hostCache, trustService, searchManager, uploadManager, fileManager, connectionEstablisher,
|
||||
certificateManager, chatServer, collectionManager, isVisible)
|
||||
|
||||
|
@ -493,6 +499,10 @@ public class Core {
|
|||
BrowseManager browseManager = new BrowseManager(i2pConnector, eventBus, me)
|
||||
eventBus.register(UIBrowseEvent.class, browseManager)
|
||||
|
||||
log.info("initializing profile fetcher")
|
||||
MWProfileFetcher profileFetcher = new MWProfileFetcher(i2pConnector, eventBus, me, profileHeaderSupplier)
|
||||
eventBus.register(UIProfileFetchEvent.class, profileFetcher)
|
||||
|
||||
log.info("initializing watched directory converter")
|
||||
watchedDirectoryConverter = new WatchedDirectoryConverter(this)
|
||||
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
package com.muwire.core.connection
|
||||
|
||||
import com.muwire.core.profile.MWProfile
|
||||
import com.muwire.core.profile.MWProfileHeader
|
||||
import net.i2p.I2PException
|
||||
import net.i2p.data.ByteArray
|
||||
|
||||
import java.nio.charset.StandardCharsets
|
||||
import java.nio.file.attribute.DosFileAttributes
|
||||
import java.util.concurrent.ExecutorService
|
||||
import java.util.concurrent.Executors
|
||||
import java.util.function.BiPredicate
|
||||
import java.util.function.Supplier
|
||||
import java.util.logging.Level
|
||||
import java.util.zip.DeflaterOutputStream
|
||||
import java.util.zip.GZIPInputStream
|
||||
|
@ -54,6 +56,7 @@ class ConnectionAcceptor {
|
|||
|
||||
final EventBus eventBus
|
||||
final Persona me
|
||||
final Supplier<MWProfile> myProfile
|
||||
final UltrapeerConnectionManager manager
|
||||
final MuWireSettings settings
|
||||
final I2PAcceptor acceptor
|
||||
|
@ -75,13 +78,14 @@ class ConnectionAcceptor {
|
|||
|
||||
volatile int browsed
|
||||
|
||||
ConnectionAcceptor(EventBus eventBus, Persona me, UltrapeerConnectionManager manager,
|
||||
ConnectionAcceptor(EventBus eventBus, Persona me, Supplier<MWProfile> myProfile, UltrapeerConnectionManager manager,
|
||||
MuWireSettings settings, I2PAcceptor acceptor, HostCache hostCache,
|
||||
TrustService trustService, SearchManager searchManager, UploadManager uploadManager,
|
||||
FileManager fileManager, ConnectionEstablisher establisher, CertificateManager certificateManager,
|
||||
ChatServer chatServer, CollectionManager collectionManager, BiPredicate<File,Persona> isVisible) {
|
||||
this.eventBus = eventBus
|
||||
this.me = me
|
||||
this.myProfile = myProfile
|
||||
this.manager = manager
|
||||
this.settings = settings
|
||||
this.acceptor = acceptor
|
||||
|
@ -188,6 +192,8 @@ class ConnectionAcceptor {
|
|||
case (byte)'L':
|
||||
processETTER(e)
|
||||
break
|
||||
case (byte)'A':
|
||||
procesVATAR(e)
|
||||
default:
|
||||
throw new Exception("Invalid read $read")
|
||||
}
|
||||
|
@ -775,5 +781,41 @@ class ConnectionAcceptor {
|
|||
e.close()
|
||||
}
|
||||
}
|
||||
|
||||
private void processVATAR(Endpoint e) {
|
||||
byte [] VATAR = "VATAR\r\n".getBytes(StandardCharsets.US_ASCII)
|
||||
byte [] read = new byte[VATAR.length]
|
||||
|
||||
DataInputStream dis = new DataInputStream(e.getInputStream())
|
||||
try {
|
||||
dis.readFully(read)
|
||||
if (VATAR != read)
|
||||
throw new Exception("Invalid VATAR")
|
||||
|
||||
Map<String, String> headers = DataUtil.readAllHeaders(dis)
|
||||
if (headers['Version'] != "1")
|
||||
throw new IOException("unrecognized version")
|
||||
|
||||
OutputStream os = e.getOutputStream()
|
||||
MWProfile profile = myProfile.get()
|
||||
if (profile == null) {
|
||||
os.write("404 Not Found\r\n".getBytes(StandardCharsets.US_ASCII))
|
||||
return
|
||||
}
|
||||
|
||||
os.write("200 OK\r\n".getBytes(StandardCharsets.US_ASCII))
|
||||
|
||||
ByteArrayOutputStream baos = new ByteArrayInputStream()
|
||||
profile.write(baos)
|
||||
byte [] payload = baos.toByteArray()
|
||||
os.write("Length:${payload.length}\r\n".getBytes(StandardCharsets.US_ASCII))
|
||||
os.write("\r\n".getBytes(StandardCharsets.US_ASCII))
|
||||
os.write(payload)
|
||||
} catch (Exception bad) {
|
||||
log.log(Level.WARNING, "failed to process AVATAR", bad)
|
||||
} finally {
|
||||
e.close()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
package com.muwire.core.profile
|
||||
|
||||
import com.muwire.core.Event
|
||||
import com.muwire.core.Persona
|
||||
|
||||
class MWProfileFetchEvent extends Event {
|
||||
MWProfileFetchStatus status
|
||||
Persona host
|
||||
UUID uuid
|
||||
MWProfile profile
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
package com.muwire.core.profile
|
||||
|
||||
import com.muwire.core.Constants
|
||||
import com.muwire.core.EventBus
|
||||
import com.muwire.core.Persona
|
||||
import com.muwire.core.connection.Endpoint
|
||||
import com.muwire.core.connection.I2PConnector
|
||||
import com.muwire.core.util.DataUtil
|
||||
import groovy.util.logging.Log
|
||||
|
||||
import java.nio.charset.StandardCharsets
|
||||
import java.util.concurrent.Executor
|
||||
import java.util.concurrent.Executors
|
||||
import java.util.function.Supplier
|
||||
import java.util.logging.Level
|
||||
|
||||
@Log
|
||||
class MWProfileFetcher {
|
||||
|
||||
private final I2PConnector connector
|
||||
private final EventBus eventBus
|
||||
private final Persona me
|
||||
private final Supplier<MWProfileHeader> myProfileHeader
|
||||
|
||||
private final Executor fetcherThread = Executors.newCachedThreadPool()
|
||||
|
||||
MWProfileFetcher(I2PConnector connector, EventBus eventBus,
|
||||
Persona me, Supplier<MWProfileHeader> myProfileHeader) {
|
||||
this.connector = connector
|
||||
this.eventBus = eventBus
|
||||
this.me = me
|
||||
this.myProfileHeader = myProfileHeader
|
||||
}
|
||||
|
||||
void onUIProfileFetchEvent(UIProfileFetchEvent e) {
|
||||
fetcherThread.execute({
|
||||
Endpoint endpoint = null
|
||||
try {
|
||||
eventBus.publish(new MWProfileFetchEvent(host: e.host, status: MWProfileFetchStatus.CONNECTING, uuid: e.uuid))
|
||||
endpoint = connector.connect(e.host.destination)
|
||||
|
||||
|
||||
OutputStream os = endpoint.getOutputStream()
|
||||
os.write("AVATAR\r\n".getBytes(StandardCharsets.US_ASCII))
|
||||
os.write("Version:1\r\n".getBytes(StandardCharsets.US_ASCII))
|
||||
os.write("Persona:${me.toBase64()}\r\n".getBytes(StandardCharsets.US_ASCII))
|
||||
MWProfileHeader header = myProfileHeader.get()
|
||||
if (header != null)
|
||||
os.write("ProfileHeader:${header.toBase64()}\r\n".getBytes(StandardCharsets.US_ASCII))
|
||||
os.write("\r\n".getBytes(StandardCharsets.US_ASCII))
|
||||
|
||||
InputStream is = endpoint.getInputStream()
|
||||
String code = DataUtil.readTillRN(is)
|
||||
if (!code.startsWith("200"))
|
||||
throw new IOException("invalid code $code")
|
||||
|
||||
Map<String,String> headers = DataUtil.readAllHeaders(is)
|
||||
|
||||
if (!headers.containsKey("Length"))
|
||||
throw new IOException("No length header")
|
||||
|
||||
int length = Integer.parseInt(headers['Length'])
|
||||
if (length > Constants.MAX_PROFILE_LENGTH)
|
||||
throw new IOException("profile too large $length")
|
||||
|
||||
eventBus.publish(new MWProfileFetchEvent(host: e.host, status: MWProfileFetchStatus.FETCHING, uuid: e.uuid))
|
||||
byte[] payload = new byte[length]
|
||||
DataInputStream dis = new DataInputStream(is)
|
||||
dis.readFully(payload)
|
||||
MWProfile profile = new MWProfile(new ByteArrayInputStream(payload))
|
||||
if (profile.getHeader().getPersona() != e.host)
|
||||
throw new Exception("profile and host mismatch")
|
||||
eventBus.publish(new MWProfileFetchEvent(host: e.host, status: MWProfileFetchStatus.FINISHED,
|
||||
uuid: e.uuid, profile: profile))
|
||||
} catch (Exception bad) {
|
||||
log.log(Level.WARNING, "profile fetch failed", bad)
|
||||
eventBus.publish(new MWProfileFetchEvent(host: e.host, status: MWProfileFetchStatus.FAILED, uuid: e.uuid))
|
||||
} finally {
|
||||
endpoint?.close()
|
||||
}
|
||||
} as Runnable)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
package com.muwire.core.profile
|
||||
|
||||
import com.muwire.core.Event
|
||||
import com.muwire.core.Persona
|
||||
|
||||
class UIProfileFetchEvent extends Event {
|
||||
UUID uuid
|
||||
Persona host
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
package com.muwire.core.profile;
|
||||
|
||||
public enum MWProfileFetchStatus {
|
||||
CONNECTING, FETCHING, FAILED, FINISHED
|
||||
}
|
|
@ -99,7 +99,7 @@ class ConnectionAcceptorTest {
|
|||
uploadManager = uploadManagerMock.proxyInstance()
|
||||
connectionEstablisher = connectionEstablisherMock.proxyInstance()
|
||||
|
||||
acceptor = new ConnectionAcceptor(eventBus, null, connectionManager, settings, i2pAcceptor,
|
||||
acceptor = new ConnectionAcceptor(eventBus, null, null, connectionManager, settings, i2pAcceptor,
|
||||
hostCache, trustService, searchManager, uploadManager, null, connectionEstablisher, null, null, null,
|
||||
{f, p -> true} as BiPredicate)
|
||||
acceptor.start()
|
||||
|
|
|
@ -243,4 +243,9 @@ mvcGroups {
|
|||
view = 'com.muwire.gui.profile.EditProfileView'
|
||||
controller = 'com.muwire.gui.profile.EditProfileController'
|
||||
}
|
||||
'view-profile' {
|
||||
model = 'com.muwire.gui.profile.ViewProfileModel'
|
||||
view = 'com.muwire.gui.profile.ViewProfileView'
|
||||
controller = 'com.muwire.gui.profile.ViewProfileController'
|
||||
}
|
||||
}
|
||||
|
|
|
@ -90,6 +90,7 @@ class SearchTabController {
|
|||
def sender = view.selectedSender()
|
||||
if (sender == null)
|
||||
return
|
||||
sender = sender.getPersona()
|
||||
String reason = JOptionPane.showInputDialog("Enter reason (optional)")
|
||||
core.eventBus.publish( new TrustEvent(persona : sender, level : TrustLevel.TRUSTED, reason : reason))
|
||||
}
|
||||
|
@ -99,16 +100,25 @@ class SearchTabController {
|
|||
def sender = view.selectedSender()
|
||||
if (sender == null)
|
||||
return
|
||||
sender = sender.getPersona()
|
||||
String reason = JOptionPane.showInputDialog("Enter reason (optional)")
|
||||
core.eventBus.publish( new TrustEvent(persona : sender, level : TrustLevel.DISTRUSTED, reason : reason))
|
||||
}
|
||||
|
||||
@ControllerAction
|
||||
void neutral() {
|
||||
void viewProfile() {
|
||||
def sender = view.selectedSender()
|
||||
if (sender == null)
|
||||
return
|
||||
core.eventBus.publish( new TrustEvent(persona : sender, level : TrustLevel.NEUTRAL))
|
||||
|
||||
UUID uuid = UUID.randomUUID()
|
||||
def params = [:]
|
||||
params.core = model.core
|
||||
params.persona = sender.getPersona()
|
||||
params.uuid = uuid
|
||||
params.profileTitle = sender.getTitle()
|
||||
|
||||
mvcGroup.createMVCGroup("view-profile", uuid.toString(), params)
|
||||
}
|
||||
|
||||
@ControllerAction
|
||||
|
@ -117,6 +127,7 @@ class SearchTabController {
|
|||
if (sender == null)
|
||||
return
|
||||
|
||||
sender = sender.getPersona()
|
||||
String groupId = UUID.randomUUID().toString()
|
||||
Map<String,Object> params = new HashMap<>()
|
||||
params['host'] = sender
|
||||
|
@ -131,6 +142,7 @@ class SearchTabController {
|
|||
if (sender == null)
|
||||
return
|
||||
|
||||
sender = sender.getPersona()
|
||||
UUID uuid = UUID.randomUUID()
|
||||
def params = [:]
|
||||
params['fileName'] = sender.getHumanReadableName()
|
||||
|
@ -147,6 +159,7 @@ class SearchTabController {
|
|||
if (sender == null)
|
||||
return
|
||||
|
||||
sender = sender.getPersona()
|
||||
Feed feed = new Feed(sender)
|
||||
feed.setAutoDownload(core.muOptions.defaultFeedAutoDownload)
|
||||
feed.setSequential(core.muOptions.defaultFeedSequential)
|
||||
|
@ -163,6 +176,7 @@ class SearchTabController {
|
|||
if (sender == null)
|
||||
return
|
||||
|
||||
sender = sender.getPersona()
|
||||
def parent = mvcGroup.parentGroup
|
||||
parent.controller.startChat(sender)
|
||||
parent.view.showChatWindow.call()
|
||||
|
@ -170,7 +184,8 @@ class SearchTabController {
|
|||
|
||||
@ControllerAction
|
||||
void message() {
|
||||
Persona recipient = view.selectedSender()
|
||||
Persona recipient = view.selectedSender()?.getPersona()
|
||||
|
||||
if (recipient == null)
|
||||
return
|
||||
|
||||
|
@ -182,7 +197,7 @@ class SearchTabController {
|
|||
|
||||
@ControllerAction
|
||||
void copyFullID() {
|
||||
Persona sender = view.selectedSender()
|
||||
Persona sender = view.selectedSender()?.getPersona()
|
||||
if (sender == null)
|
||||
return
|
||||
CopyPasteSupport.copyToClipboard(sender.toBase64())
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
package com.muwire.gui.profile
|
||||
|
||||
import com.muwire.core.trust.TrustEvent
|
||||
import com.muwire.core.trust.TrustLevel
|
||||
import griffon.core.artifact.GriffonController
|
||||
import griffon.core.controller.ControllerAction
|
||||
import griffon.inject.MVCMember
|
||||
import griffon.metadata.ArtifactProviderFor
|
||||
|
||||
import static com.muwire.gui.Translator.trans
|
||||
|
||||
import javax.annotation.Nonnull
|
||||
import javax.swing.JOptionPane
|
||||
|
||||
@ArtifactProviderFor(GriffonController)
|
||||
class ViewProfileController {
|
||||
|
||||
@MVCMember @Nonnull
|
||||
ViewProfileModel model
|
||||
@MVCMember @Nonnull
|
||||
ViewProfileView view
|
||||
|
||||
@ControllerAction
|
||||
void fetch() {
|
||||
model.register()
|
||||
}
|
||||
|
||||
@ControllerAction
|
||||
void addContact() {
|
||||
String reason = JOptionPane.showInputDialog(trans("ENTER_REASON_OPTIONAL"))
|
||||
model.core.eventBus.publish(new TrustEvent(persona: model.persona, level: TrustLevel.TRUSTED, reason: reason))
|
||||
}
|
||||
|
||||
@ControllerAction
|
||||
void block() {
|
||||
String reason = JOptionPane.showInputDialog(trans("ENTER_REASON_OPTIONAL"))
|
||||
model.core.eventBus.publish(new TrustEvent(persona: model.persona, level: TrustLevel.DISTRUSTED, reason: reason))
|
||||
}
|
||||
|
||||
@ControllerAction
|
||||
void close() {
|
||||
view.window.setVisible(false)
|
||||
mvcGroup.destroy()
|
||||
}
|
||||
}
|
|
@ -271,7 +271,7 @@ NEUTRAL=Neutral
|
|||
DISTRUST=Distrust
|
||||
COPY_FULL_ID=Copy Full ID
|
||||
NO_PROFILE=This user does not have a profile
|
||||
|
||||
VIEW_PROFILE=View Profile
|
||||
|
||||
# results table (group by sender)
|
||||
DIRECT_SOURCES=Direct Sources
|
||||
|
@ -712,6 +712,15 @@ PROFILE_EDITOR_ERROR_NO_IMAGE=Please select an avatar or generate one
|
|||
PROFILE_EDITOR_ERROR_LARGE_TITLE=Profile title is too long
|
||||
PROFILE_EDITOR_ERROR_LARGE_BODY=Profile is too long
|
||||
|
||||
## View Profile Frame
|
||||
PROFILE_VIEWER_TITLE=Profile of {0}
|
||||
PROFILE_VIEWER_HEADER=Profile Title
|
||||
PROFILE_VIEWER_HEADER_MISSING=No profile title available. Fetch profile to see the title.
|
||||
PROFILE_VIEWER_FETCH=Fetch Profile
|
||||
PROFILE_VIEWER_AVATAR=User Avatar
|
||||
PROFILE_VIEWER_PROFILE=Profile
|
||||
PROFILE_VIEWER_BLOCK=Block
|
||||
|
||||
## Tooltips
|
||||
|
||||
TOOLTIP_FILE_FEED_DISABLED=Your file feed is disabled
|
||||
|
@ -727,6 +736,7 @@ TOOLTIP_BROWSE_COLLECTIONS_SENDER=Browse all the sender's collections
|
|||
TOOLTIP_SENDER_COPY_FULL=Copy the full ID of the sender
|
||||
TOOLTIP_ADD_CONTACT_SENDER=Add the sender as a trusted contact
|
||||
TOOLTIP_DISTRUST_SENDER=Block the sender
|
||||
TOOLTIP_VIEW_PROFILE=View the profile of the sender
|
||||
TOOLTIP_DOWNLOAD_FILE=Download the selected results
|
||||
TOOLTIP_DOWNLOAD_SEQUENTIALLY=Download in order (enables preview)
|
||||
TOOLTIP_VIEW_DETAILS_RESULT=View details about the result
|
||||
|
@ -886,5 +896,10 @@ TOOLTIP_CHAT_SERVERS_CONNECT=Connect to server now
|
|||
TOOLTIP_PROFILE_EDITOR=Edit Profile
|
||||
TOOLTIP_PROFILE_EDITOR_GENERATE=Generate an avatar from your MuWire ID
|
||||
|
||||
### Tooltips for the profile viewer
|
||||
TOOLTIP_PROFILE_VIEWER_FETCH=Fetch the full profile of this user
|
||||
TOOLTIP_PROFILE_VIEWER_ADD_CONTACT=Add this user as a trusted contact
|
||||
TOOLTIP_PROFILE_VIEWER_BLOCK=Block this user
|
||||
|
||||
## Test string
|
||||
TEST_PLURALIZABLE_STRING={count, plural, one {You have {count} item.} other {You have {count} items.}}
|
||||
|
|
|
@ -39,6 +39,7 @@ class SearchTabModel {
|
|||
@Observable boolean chatActionEnabled
|
||||
@Observable boolean subscribeActionEnabled
|
||||
@Observable boolean messageActionEnabled
|
||||
@Observable boolean viewProfileActionEnabled
|
||||
@Observable boolean viewDetailsActionEnabled
|
||||
@Observable boolean groupedByFile
|
||||
|
||||
|
@ -62,7 +63,6 @@ class SearchTabModel {
|
|||
|
||||
def results2 = []
|
||||
def allResults2 = new LinkedHashSet()
|
||||
def senders2 = []
|
||||
volatile String[] filter
|
||||
volatile Filterer filterer
|
||||
@Observable boolean clearFilterActionEnabled
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
package com.muwire.gui.profile
|
||||
|
||||
import com.muwire.core.Core
|
||||
import com.muwire.core.Persona
|
||||
import com.muwire.core.profile.MWProfileFetchEvent
|
||||
import com.muwire.core.profile.MWProfileFetchStatus
|
||||
import com.muwire.core.profile.MWProfileHeader
|
||||
import com.muwire.core.profile.UIProfileFetchEvent
|
||||
import griffon.core.artifact.GriffonModel
|
||||
import griffon.inject.MVCMember
|
||||
import griffon.metadata.ArtifactProviderFor
|
||||
import griffon.transform.Observable
|
||||
|
||||
import javax.annotation.Nonnull
|
||||
|
||||
@ArtifactProviderFor(GriffonModel)
|
||||
class ViewProfileModel {
|
||||
@MVCMember @Nonnull
|
||||
ViewProfileView view
|
||||
|
||||
Core core
|
||||
Persona persona
|
||||
UUID uuid
|
||||
String profileTitle
|
||||
|
||||
@Observable MWProfileFetchStatus status
|
||||
|
||||
private boolean registered
|
||||
|
||||
void mvcGroupInit(Map<String, String> args) {
|
||||
}
|
||||
|
||||
void register() {
|
||||
if (registered)
|
||||
return
|
||||
registered = true
|
||||
core.getEventBus().register(MWProfileFetchEvent.class, this)
|
||||
core.getEventBus().publish(new UIProfileFetchEvent(uuid: uuid, host: persona))
|
||||
}
|
||||
|
||||
void mvcGroupDestroy() {
|
||||
if (registered)
|
||||
core.getEventBus().unregister(MWProfileFetchEvent.class, this)
|
||||
}
|
||||
|
||||
void onMWProfileFetchEvent(MWProfileFetchEvent event) {
|
||||
if (uuid != event.uuid)
|
||||
return
|
||||
runInsideUIAsync {
|
||||
status = event.status
|
||||
if (status == MWProfileFetchStatus.FINISHED)
|
||||
view.profileFetched(event.profile)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -126,6 +126,7 @@ class SearchTabView {
|
|||
panel (border : etchedBorder()){
|
||||
button(text : trans("ADD_CONTACT"), toolTipText: trans("TOOLTIP_ADD_CONTACT_SENDER"), enabled: bind {model.trustButtonsEnabled }, trustAction)
|
||||
button(text : trans("DISTRUST"), toolTipText: trans("TOOLTIP_DISTRUST_SENDER"), enabled : bind {model.trustButtonsEnabled}, distrustAction)
|
||||
button(text : trans("VIEW_PROFILE"), toolTipText: trans("TOOLTIP_VIEW_PROFILE"), enabled: bind {model.viewProfileActionEnabled}, viewProfileAction)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -412,6 +413,7 @@ class SearchTabView {
|
|||
int row = selectedSenderRow()
|
||||
if (row < 0) {
|
||||
model.trustButtonsEnabled = false
|
||||
model.viewProfileActionEnabled = false
|
||||
model.browseActionEnabled = false
|
||||
model.subscribeActionEnabled = false
|
||||
model.browseCollectionsActionEnabled = false
|
||||
|
@ -428,6 +430,7 @@ class SearchTabView {
|
|||
model.subscribeActionEnabled = bucket.results[0].feed &&
|
||||
model.core.feedManager.getFeed(sender) == null
|
||||
model.trustButtonsEnabled = true
|
||||
model.viewProfileActionEnabled = true
|
||||
|
||||
model.results.clear()
|
||||
model.results.addAll(bucket.results)
|
||||
|
@ -469,6 +472,7 @@ class SearchTabView {
|
|||
model.browseCollectionsActionEnabled = false
|
||||
model.chatActionEnabled = false
|
||||
model.messageActionEnabled = false
|
||||
model.viewProfileActionEnabled = false
|
||||
|
||||
return
|
||||
}
|
||||
|
@ -525,6 +529,7 @@ class SearchTabView {
|
|||
model.tab = parent.indexOfComponent(pane)
|
||||
parent.removeTabAt(model.tab)
|
||||
model.trustButtonsEnabled = false
|
||||
model.viewProfileActionEnabled = false
|
||||
model.downloadActionEnabled = false
|
||||
resultDetails.values().each {it.destroy()}
|
||||
mvcGroup.destroy()
|
||||
|
@ -680,14 +685,11 @@ class SearchTabView {
|
|||
}
|
||||
}
|
||||
|
||||
Persona selectedSender() {
|
||||
PersonaOrProfile selectedSender() {
|
||||
int row = selectedSenderRow()
|
||||
if (row < 0)
|
||||
return null
|
||||
if (model.groupedByFile)
|
||||
return model.senders2[row]?.sender
|
||||
else
|
||||
return model.senders[row]?.sender
|
||||
return model.senders[row]
|
||||
}
|
||||
|
||||
def showSenderGrouping = {
|
||||
|
|
|
@ -0,0 +1,132 @@
|
|||
package com.muwire.gui.profile
|
||||
|
||||
import com.muwire.core.profile.MWProfile
|
||||
import com.muwire.gui.HTMLSanitizer
|
||||
|
||||
import javax.imageio.ImageIO
|
||||
import javax.swing.JLabel
|
||||
import javax.swing.JPanel
|
||||
import javax.swing.JTextArea
|
||||
import javax.swing.border.TitledBorder
|
||||
import java.awt.BorderLayout
|
||||
import java.awt.Dimension
|
||||
import java.awt.Window
|
||||
import java.awt.event.WindowAdapter
|
||||
import java.awt.event.WindowEvent
|
||||
|
||||
import static com.muwire.gui.Translator.trans
|
||||
|
||||
import griffon.core.GriffonApplication
|
||||
import griffon.core.artifact.GriffonView
|
||||
import griffon.inject.MVCMember
|
||||
import griffon.metadata.ArtifactProviderFor
|
||||
|
||||
import javax.annotation.Nonnull
|
||||
import javax.inject.Inject
|
||||
import javax.swing.JFrame
|
||||
|
||||
@ArtifactProviderFor(GriffonView)
|
||||
class ViewProfileView {
|
||||
@MVCMember @Nonnull
|
||||
ViewProfileModel model
|
||||
@MVCMember @Nonnull
|
||||
ViewProfileController controller
|
||||
@MVCMember @Nonnull
|
||||
FactoryBuilderSupport builder
|
||||
@Inject
|
||||
GriffonApplication application
|
||||
|
||||
JFrame window
|
||||
JFrame mainFrame
|
||||
|
||||
JPanel mainPanel
|
||||
JLabel titleLabel
|
||||
JPanel imagePanel
|
||||
JTextArea bodyArea
|
||||
|
||||
void initUI() {
|
||||
mainFrame = application.windowManager.findWindow("main-frame")
|
||||
def mainDim = mainFrame.getSize()
|
||||
|
||||
int dimX = Math.max(1100, (int)(mainDim.getWidth() / 2))
|
||||
int dimY = Math.max(700, (int)(mainDim.getHeight() / 2))
|
||||
|
||||
window = builder.frame(visible: false, defaultCloseOperation: JFrame.DISPOSE_ON_CLOSE,
|
||||
iconImage: builder.imageIcon("/MuWire-48x48.png").image,
|
||||
title: trans("PROFILE_VIEWER_TITLE", model.persona.getHumanReadableName())) {
|
||||
borderLayout()
|
||||
panel(border: titledBorder(title : trans("PROFILE_VIEWER_HEADER"), border: etchedBorder(),
|
||||
titlePosition: TitledBorder.TOP), constraints: BorderLayout.NORTH) {
|
||||
if (model.profileTitle == null)
|
||||
titleLabel = label(text: trans("PROFILE_VIEWER_HEADER_MISSING"))
|
||||
else
|
||||
titleLabel = label(text: model.profileTitle)
|
||||
}
|
||||
mainPanel = panel(constraints: BorderLayout.CENTER) {
|
||||
cardLayout()
|
||||
panel(constraints: "fetch-profile") {
|
||||
button(text: trans("PROFILE_VIEWER_FETCH"), toolTipText: trans("TOOLTIP_PROFILE_VIEWER_FETCH"),
|
||||
fetchAction)
|
||||
}
|
||||
panel(constraints: "full-profile") {
|
||||
gridLayout(rows: 1, cols: 2)
|
||||
panel(border: titledBorder(title: trans("PROFILE_VIEWER_AVATAR"), border: etchedBorder(),
|
||||
titlePosition: TitledBorder.TOP)) {
|
||||
imagePanel = panel()
|
||||
}
|
||||
panel(border: titledBorder(title: trans("PROFILE_VIEWER_PROFILE"), border: etchedBorder(),
|
||||
titlePosition: TitledBorder.TOP)) {
|
||||
scrollPane {
|
||||
bodyArea = textArea(editable: false, lineWrap: true, wrapStyleWord: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
panel(constraints: BorderLayout.SOUTH) {
|
||||
borderLayout()
|
||||
panel(constraints: BorderLayout.WEST) {
|
||||
label(text : bind { model.status == null ? "" : trans(model.status.name())})
|
||||
}
|
||||
panel(constraints: BorderLayout.CENTER) {
|
||||
button(text: trans("ADD_CONTACT"), toolTipText: trans("TOOLTIP_PROFILE_VIEWER_ADD_CONTACT"),
|
||||
addContactAction)
|
||||
button(text: trans("PROFILE_VIEWER_BLOCK"), toolTipText: trans("TOOLTIP_PROFILE_VIEWER_BLOCK"),
|
||||
blockAction)
|
||||
}
|
||||
panel(constraints: BorderLayout.EAST) {
|
||||
button(text : trans("CLOSE"), closeAction)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
window.setPreferredSize([dimX, dimY] as Dimension)
|
||||
}
|
||||
|
||||
void mvcGroupInit(Map<String, String> params) {
|
||||
window.addWindowListener(new WindowAdapter() {
|
||||
@Override
|
||||
void windowClosed(WindowEvent e) {
|
||||
mvcGroup.destroy()
|
||||
}
|
||||
})
|
||||
|
||||
window.pack()
|
||||
window.setLocationRelativeTo(mainFrame)
|
||||
window.setVisible(true)
|
||||
}
|
||||
|
||||
void profileFetched(MWProfile profile) {
|
||||
mainPanel.getLayout().show(mainPanel, "full-profile")
|
||||
titleLabel.setText(HTMLSanitizer.sanitize(profile.getHeader().getTitle()))
|
||||
bodyArea.setText(profile.getBody())
|
||||
|
||||
def rawImage = ImageIO.read(new ByteArrayInputStream(profile.getImage()))
|
||||
def mainImage = ImageScaler.scaleToMax(rawImage)
|
||||
|
||||
def imgDim = imagePanel.getSize()
|
||||
imagePanel.getGraphics().drawImage(mainImage,
|
||||
(int)(imgDim.getWidth() / 2) - (int)(mainImage.getWidth() / 2),
|
||||
(int)(imgDim.getHeight() / 2) - (int)(mainImage.getHeight() / 2),
|
||||
null)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue