Consistent groups list
This commit is contained in:
parent
bd4132e6a7
commit
316604d3d5
|
@ -15,6 +15,10 @@ public class BaseChatInfo {
|
|||
return supergroupId;
|
||||
}
|
||||
|
||||
public int getSupergroupIdInt() {
|
||||
return TransferUtils.chatIdToChatEntityId(supergroupId);
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
package it.cavallium;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.StringJoiner;
|
||||
|
||||
public class ItemUpdate<T> {
|
||||
private final boolean removed;
|
||||
private final T item;
|
||||
|
||||
public ItemUpdate(boolean removed, T item) {
|
||||
this.removed = removed;
|
||||
this.item = item;
|
||||
}
|
||||
|
||||
public boolean isRemoved() {
|
||||
return removed;
|
||||
}
|
||||
|
||||
public T getItem() {
|
||||
return item;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ItemUpdate<?> that = (ItemUpdate<?>) o;
|
||||
|
||||
if (removed != that.removed) {
|
||||
return false;
|
||||
}
|
||||
return Objects.equals(item, that.item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = (removed ? 1 : 0);
|
||||
result = 31 * result + (item != null ? item.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new StringJoiner(", ", ItemUpdate.class.getSimpleName() + "[", "]")
|
||||
.add("removed=" + removed)
|
||||
.add("item=" + item)
|
||||
.toString();
|
||||
}
|
||||
}
|
|
@ -7,6 +7,7 @@ import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber;
|
|||
import java.io.IOException;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.function.Function;
|
||||
import javafx.application.Platform;
|
||||
import javafx.event.ActionEvent;
|
||||
|
@ -16,13 +17,17 @@ import javafx.scene.Node;
|
|||
import javafx.scene.control.Alert;
|
||||
import javafx.scene.control.Alert.AlertType;
|
||||
import javafx.scene.control.ButtonType;
|
||||
import javafx.scene.control.ChoiceBox;
|
||||
import javafx.scene.control.ComboBox;
|
||||
import javafx.scene.control.Dialog;
|
||||
import javafx.scene.control.ListView;
|
||||
import javafx.scene.control.TextField;
|
||||
import javafx.scene.control.TextInputDialog;
|
||||
import javafx.scene.input.InputMethodEvent;
|
||||
import javafx.scene.layout.Pane;
|
||||
import javafx.scene.text.Text;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.slf4j.event.Level;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.core.scheduler.Schedulers;
|
||||
|
||||
|
@ -31,8 +36,8 @@ public class PrimaryController {
|
|||
@FXML private Pane main;
|
||||
@FXML private TextField phoneNumber;
|
||||
@FXML private ListView<Text> userbotsList;
|
||||
@FXML private ComboBox<BaseChatInfo> sourceGroupCombo;
|
||||
@FXML private ComboBox<BaseChatInfo> destGroupCombo;
|
||||
@FXML private ChoiceBox<BaseChatInfo> sourceGroupCombo;
|
||||
@FXML private ChoiceBox<BaseChatInfo> destGroupCombo;
|
||||
@FXML private ListView<LogEntry> log;
|
||||
|
||||
@FXML
|
||||
|
@ -57,6 +62,36 @@ public class PrimaryController {
|
|||
destItems.add(destSupergroups);
|
||||
}
|
||||
|
||||
App
|
||||
.getTransferService()
|
||||
.subscribeAdminSupergroups()
|
||||
.subscribeOn(Schedulers.single())
|
||||
.subscribe((supergroupInfo) -> {
|
||||
Platform.runLater(() -> {
|
||||
var srcItems2 = sourceGroupCombo.getItems();
|
||||
var destItems2 = destGroupCombo.getItems();
|
||||
if (userbotsList.getItems().isEmpty()) {
|
||||
srcItems2.clear();
|
||||
destItems2.clear();
|
||||
} else {
|
||||
boolean added = false;
|
||||
if (supergroupInfo.getItem().canRestrictMembers()) {
|
||||
if (!srcItems2.contains(supergroupInfo.getItem().getBaseChatInfo())) {
|
||||
added |= srcItems2.add(supergroupInfo.getItem().getBaseChatInfo());
|
||||
}
|
||||
}
|
||||
if (supergroupInfo.getItem().canInviteUsers()) {
|
||||
if (!destItems2.contains(supergroupInfo.getItem().getBaseChatInfo())) {
|
||||
added |= destItems2.add(supergroupInfo.getItem().getBaseChatInfo());
|
||||
}
|
||||
}
|
||||
if (added) {
|
||||
App.getLogService().append(Level.INFO, "Found group \"" + supergroupInfo.getItem().getBaseChatInfo().getTitle() + "\"");
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
App.getLogService().listenUpdates().subscribeOn(Schedulers.boundedElastic()).flatMap(logString -> MonoFxUtils.runLater(() -> {
|
||||
if (log.getItems().size() >= App.getLogService().getMaxSize()) {
|
||||
log.getItems().remove(0);
|
||||
|
@ -139,6 +174,10 @@ public class PrimaryController {
|
|||
return false;
|
||||
}
|
||||
});
|
||||
if (userbotsList.getItems().isEmpty()) {
|
||||
this.sourceGroupCombo.getItems().clear();
|
||||
this.destGroupCombo.getItems().clear();
|
||||
}
|
||||
return Mono.empty();
|
||||
});
|
||||
}).handle((result, sink) -> {
|
||||
|
@ -156,12 +195,14 @@ public class PrimaryController {
|
|||
);
|
||||
return MonoFxUtils.showAndWait(alert);
|
||||
})).doOnSuccess((_v) -> {
|
||||
userbotsList
|
||||
.getItems()
|
||||
.add(0,
|
||||
new Text(phoneNumberPrettyText)
|
||||
);
|
||||
this.phoneNumber.setText("");
|
||||
Platform.runLater(() -> {
|
||||
userbotsList
|
||||
.getItems()
|
||||
.add(0,
|
||||
new Text(phoneNumberPrettyText)
|
||||
);
|
||||
this.phoneNumber.setText("");
|
||||
});
|
||||
})
|
||||
.doOnTerminate(this::enableClicks)
|
||||
.subscribe(_v -> {}, error -> {
|
||||
|
@ -268,5 +309,36 @@ public class PrimaryController {
|
|||
|
||||
@FXML
|
||||
public void onDoTransfer(ActionEvent actionEvent) {
|
||||
if (sourceGroupCombo.getSelectionModel().getSelectedItem() == null || !(sourceGroupCombo.getSelectionModel().getSelectedItem() instanceof BaseChatInfo)) {
|
||||
MonoFxUtils
|
||||
.showAndWait(new Alert(AlertType.ERROR, "You must select a source group", ButtonType.CLOSE))
|
||||
.subscribe();
|
||||
return;
|
||||
}
|
||||
if (destGroupCombo.getSelectionModel().getSelectedItem() == null || !(destGroupCombo.getSelectionModel().getSelectedItem() instanceof BaseChatInfo)) {
|
||||
MonoFxUtils
|
||||
.showAndWait(new Alert(AlertType.ERROR, "You must select a destination group", ButtonType.CLOSE))
|
||||
.subscribe();
|
||||
return;
|
||||
}
|
||||
disableClicks();
|
||||
App
|
||||
.getTransferService()
|
||||
.transferMembers(sourceGroupCombo.getSelectionModel().getSelectedItem(),
|
||||
destGroupCombo.getSelectionModel().getSelectedItem(),
|
||||
userStatus -> {
|
||||
return Mono.empty();
|
||||
}
|
||||
)
|
||||
.onErrorResume(error -> MonoFxUtils.runLater(() -> {
|
||||
var alert = new Alert(AlertType.ERROR, "Error during transfer", ButtonType.CLOSE);
|
||||
alert.setContentText(error.getLocalizedMessage());
|
||||
return MonoFxUtils.showAndWait(alert);
|
||||
}).then())
|
||||
.subscribe(_v -> {}, _v -> {
|
||||
|
||||
}, () -> {
|
||||
enableClicks();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
package it.cavallium;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.StringJoiner;
|
||||
|
||||
public class SupergroupInfo {
|
||||
|
||||
private final BaseChatInfo baseChatInfo;
|
||||
private final boolean canRestrictMembers;
|
||||
private final boolean canInviteUsers;
|
||||
|
||||
public SupergroupInfo(BaseChatInfo baseChatInfo, boolean canRestrictMembers, boolean canInviteUsers) {
|
||||
this.baseChatInfo = baseChatInfo;
|
||||
this.canRestrictMembers = canRestrictMembers;
|
||||
this.canInviteUsers = canInviteUsers;
|
||||
}
|
||||
|
||||
public BaseChatInfo getBaseChatInfo() {
|
||||
return baseChatInfo;
|
||||
}
|
||||
|
||||
public boolean canInviteUsers() {
|
||||
return canInviteUsers;
|
||||
}
|
||||
|
||||
public boolean canRestrictMembers() {
|
||||
return canRestrictMembers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SupergroupInfo that = (SupergroupInfo) o;
|
||||
|
||||
if (canRestrictMembers != that.canRestrictMembers) {
|
||||
return false;
|
||||
}
|
||||
if (canInviteUsers != that.canInviteUsers) {
|
||||
return false;
|
||||
}
|
||||
return Objects.equals(baseChatInfo, that.baseChatInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = baseChatInfo != null ? baseChatInfo.hashCode() : 0;
|
||||
result = 31 * result + (canRestrictMembers ? 1 : 0);
|
||||
result = 31 * result + (canInviteUsers ? 1 : 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new StringJoiner(", ", SupergroupInfo.class.getSimpleName() + "[", "]")
|
||||
.add("baseChatInfo=" + baseChatInfo)
|
||||
.add("canRestrictMembers=" + canRestrictMembers)
|
||||
.add("canInviteUsers=" + canInviteUsers)
|
||||
.toString();
|
||||
}
|
||||
}
|
|
@ -16,11 +16,12 @@ import it.tdlight.jni.TdApi.UpdateSupergroup;
|
|||
import it.tdlight.jni.TdApi.UpdateSupergroupFullInfo;
|
||||
import it.tdlight.tdlibsession.td.TdResult;
|
||||
import it.tdlight.tdlibsession.td.easy.AsyncTdEasy;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import org.reactivestreams.Publisher;
|
||||
import reactor.core.publisher.EmitterProcessor;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.core.scheduler.Scheduler;
|
||||
import reactor.core.scheduler.Schedulers;
|
||||
|
@ -31,6 +32,7 @@ public class TransferClient {
|
|||
private final ConcurrentHashMap<Integer, Supergroup> supergroupInfos = new ConcurrentHashMap<>();
|
||||
private final ConcurrentHashMap<Integer, SupergroupFullInfo> supergroupFullInfos = new ConcurrentHashMap<>();
|
||||
private final ConcurrentHashMap<Long, Chat> chats = new ConcurrentHashMap<>();
|
||||
private final EmitterProcessor<ItemUpdate<SupergroupInfo>> usefulSupergroups = EmitterProcessor.create();
|
||||
private final Scheduler scheduler;
|
||||
|
||||
public TransferClient(AsyncTdEasy client) {
|
||||
|
@ -91,6 +93,8 @@ public class TransferClient {
|
|||
|
||||
private Mono<Void> onUpdateSupergroupFullInfo(int id, SupergroupFullInfo supergroupFullInfo) {
|
||||
if (supergroupFullInfos.put(id, supergroupFullInfo) == null) {
|
||||
var sgInfo = getSupergroupBaseChatInfo(id);
|
||||
sgInfo.ifPresent(t -> this.usefulSupergroups.onNext(new ItemUpdate<>(false, t)));
|
||||
// ok
|
||||
}
|
||||
return Mono.empty();
|
||||
|
@ -104,28 +108,38 @@ public class TransferClient {
|
|||
return client.<T>send(request).publishOn(scheduler);
|
||||
}
|
||||
|
||||
public Set<BaseChatInfo> getAdminSupergroups(boolean canRestrictMembers, boolean canInviteUsers) {
|
||||
public Set<BaseChatInfo> getAdminSupergroups(boolean requireCanRestrictMembers, boolean requireCanInviteUsers) {
|
||||
return supergroupFullInfos.entrySet().stream().flatMap(entry -> {
|
||||
int id = entry.getKey();
|
||||
var fullInfo = entry.getValue();
|
||||
var chatInfo = chats.get(TransferUtils.chatEntityIdToChatId(id, TChatType.SUPERGROUP));
|
||||
var baseInfo = supergroupInfos.get(id);
|
||||
if (chatInfo == null || baseInfo == null) {
|
||||
return Stream.<BaseChatInfo>empty();
|
||||
} else {
|
||||
if (baseInfo.status.getConstructor() != ChatMemberStatusAdministrator.CONSTRUCTOR) {
|
||||
return Stream.empty();
|
||||
return getSupergroupBaseChatInfo(id).filter(sgInfo -> {
|
||||
if (!sgInfo.canRestrictMembers() && requireCanRestrictMembers) {
|
||||
return false;
|
||||
}
|
||||
var adminStatus = (ChatMemberStatusAdministrator) baseInfo.status;
|
||||
if (!adminStatus.canRestrictMembers && canRestrictMembers) {
|
||||
return Stream.empty();
|
||||
if (!sgInfo.canInviteUsers() && requireCanInviteUsers) {
|
||||
return false;
|
||||
}
|
||||
if (!adminStatus.canInviteUsers && canInviteUsers) {
|
||||
return Stream.empty();
|
||||
}
|
||||
var baseChatInfo = new BaseChatInfo(id, chatInfo.title);
|
||||
return Stream.<BaseChatInfo>of(baseChatInfo);
|
||||
}
|
||||
return true;
|
||||
}).map(SupergroupInfo::getBaseChatInfo).stream();
|
||||
}).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
private Optional<SupergroupInfo> getSupergroupBaseChatInfo(int id) {
|
||||
var chatInfo = chats.get(TransferUtils.chatEntityIdToChatId(id, TChatType.SUPERGROUP));
|
||||
var baseInfo = supergroupInfos.get(id);
|
||||
if (chatInfo == null || baseInfo == null) {
|
||||
return Optional.empty();
|
||||
} else {
|
||||
if (baseInfo.status.getConstructor() != ChatMemberStatusAdministrator.CONSTRUCTOR) {
|
||||
return Optional.empty();
|
||||
}
|
||||
var adminStatus = (ChatMemberStatusAdministrator) baseInfo.status;
|
||||
var baseChatInfo = new BaseChatInfo(id, chatInfo.title);
|
||||
var sgInfo = new SupergroupInfo(baseChatInfo, adminStatus.canRestrictMembers, adminStatus.canInviteUsers);
|
||||
return Optional.of(sgInfo);
|
||||
}
|
||||
}
|
||||
|
||||
public Flux<ItemUpdate<SupergroupInfo>> subscribeAdminSupergroups() {
|
||||
return usefulSupergroups.hide();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import java.util.Set;
|
|||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
public interface TransferService {
|
||||
|
@ -26,4 +27,8 @@ public interface TransferService {
|
|||
Set<PhoneNumber> getPhoneNumbers();
|
||||
|
||||
Set<BaseChatInfo> getAdminSupergroups(boolean canRestrictMembers, boolean canInviteUsers);
|
||||
|
||||
Flux<ItemUpdate<SupergroupInfo>> subscribeAdminSupergroups();
|
||||
|
||||
Mono<Void> transferMembers(BaseChatInfo sourceGroup, BaseChatInfo destGroup, Function<UserStatus, Mono<Void>> userStatusConsumer);
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package it.cavallium;
|
|||
|
||||
import static it.cavallium.PrimaryController.getUserbotPhoneNumber;
|
||||
|
||||
import com.google.common.collect.ConcurrentHashMultiset;
|
||||
import com.google.i18n.phonenumbers.NumberParseException;
|
||||
import com.google.i18n.phonenumbers.PhoneNumberUtil;
|
||||
import com.google.i18n.phonenumbers.PhoneNumberUtil.PhoneNumberFormat;
|
||||
|
@ -35,6 +36,7 @@ import org.slf4j.LoggerFactory;
|
|||
import reactor.core.publisher.EmitterProcessor;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.core.publisher.ReplayProcessor;
|
||||
import reactor.core.scheduler.Scheduler;
|
||||
import reactor.core.scheduler.Schedulers;
|
||||
|
||||
|
@ -42,6 +44,8 @@ public class TransferServiceImpl implements TransferService {
|
|||
|
||||
private final TdClusterManager clusterManager;
|
||||
private final ConcurrentHashMap<Long, TransferClient> clients = new ConcurrentHashMap<>();
|
||||
private final ConcurrentHashMap<Integer, Set<TransferClient>> supergroupClients = new ConcurrentHashMap<>();
|
||||
private final EmitterProcessor<ItemUpdate<TransferClient>> newClients = EmitterProcessor.create();
|
||||
private final Scheduler updatesScheduler;
|
||||
private int apiId;
|
||||
private String apiHash;
|
||||
|
@ -172,7 +176,11 @@ public class TransferServiceImpl implements TransferService {
|
|||
})
|
||||
.then();
|
||||
}))
|
||||
.doOnSuccess((_v) -> clients.put(phoneNumberLong, new TransferClient(client))));
|
||||
.doOnSuccess((_v) -> {
|
||||
var newClient = new TransferClient(client);
|
||||
clients.put(phoneNumberLong, newClient);
|
||||
newClients.onNext(new ItemUpdate<>(false, newClient));
|
||||
}));
|
||||
})
|
||||
.map(_v -> AddUserBotResult.newSuccess());
|
||||
}
|
||||
|
@ -191,6 +199,7 @@ public class TransferServiceImpl implements TransferService {
|
|||
@Override
|
||||
public Mono<Void> closeUserbot(PhoneNumber phoneNumber) {
|
||||
var client = clients.remove(getLongPhoneNumber(phoneNumber));
|
||||
newClients.onNext(new ItemUpdate<>(true, client));
|
||||
if (client == null) {
|
||||
return Mono.error(new Exception("Userbot " + phoneNumber + " was not found!"));
|
||||
} else {
|
||||
|
@ -224,6 +233,31 @@ public class TransferServiceImpl implements TransferService {
|
|||
return adminSupergroups;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<ItemUpdate<SupergroupInfo>> subscribeAdminSupergroups() {
|
||||
return Flux.merge(this.newClients, Flux
|
||||
.fromStream(clients.values().stream().map(client -> new ItemUpdate<>(false, client))))
|
||||
.filter(itemClient -> !itemClient.isRemoved())
|
||||
.map(ItemUpdate::getItem)
|
||||
.map(client1 -> client1.subscribeAdminSupergroups().doOnNext(supergroupInfoItemUpdate -> {
|
||||
supergroupClients.compute(supergroupInfoItemUpdate.getItem().getBaseChatInfo().getSupergroupIdInt(), (sgId, clients) -> {
|
||||
if (clients == null) {
|
||||
clients = new HashSet<>();
|
||||
}
|
||||
clients.add(client1);
|
||||
return clients;
|
||||
});
|
||||
}))
|
||||
.flatMap(f -> f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> transferMembers(BaseChatInfo sourceGroup,
|
||||
BaseChatInfo destGroup,
|
||||
Function<UserStatus, Mono<Void>> userStatusConsumer) {
|
||||
return Mono.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> quit() {
|
||||
return Flux
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
package it.cavallium;
|
||||
|
||||
import java.util.StringJoiner;
|
||||
|
||||
public class UserStatus {
|
||||
private final String name;
|
||||
private final int id;
|
||||
private final UserStatusType statusType;
|
||||
private final String errorDescription;
|
||||
|
||||
public UserStatus(String name, int id, UserStatusType statusType, String errorDescription) {
|
||||
this.name = name;
|
||||
this.id = id;
|
||||
this.statusType = statusType;
|
||||
this.errorDescription = errorDescription;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getErrorDescription() {
|
||||
return errorDescription;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public UserStatusType getStatusType() {
|
||||
return statusType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
UserStatus that = (UserStatus) o;
|
||||
|
||||
if (id != that.id) {
|
||||
return false;
|
||||
}
|
||||
if (name != null ? !name.equals(that.name) : that.name != null) {
|
||||
return false;
|
||||
}
|
||||
if (statusType != that.statusType) {
|
||||
return false;
|
||||
}
|
||||
return errorDescription != null ? errorDescription.equals(that.errorDescription) : that.errorDescription == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = name != null ? name.hashCode() : 0;
|
||||
result = 31 * result + id;
|
||||
result = 31 * result + (statusType != null ? statusType.hashCode() : 0);
|
||||
result = 31 * result + (errorDescription != null ? errorDescription.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new StringJoiner(", ", UserStatus.class.getSimpleName() + "[", "]")
|
||||
.add("name='" + name + "'")
|
||||
.add("id=" + id)
|
||||
.add("errorType=" + statusType)
|
||||
.add("errorDescription='" + errorDescription + "'")
|
||||
.toString();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
package it.cavallium;
|
||||
|
||||
public enum UserStatusType {
|
||||
UNKNOWN
|
||||
}
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.Button?>
|
||||
<?import javafx.scene.control.ComboBox?>
|
||||
<?import javafx.scene.control.ChoiceBox?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.control.ListView?>
|
||||
<?import javafx.scene.control.Menu?>
|
||||
|
@ -95,8 +95,8 @@
|
|||
<children>
|
||||
<VBox maxHeight="-Infinity" minHeight="-Infinity" spacing="10.0" GridPane.columnSpan="2">
|
||||
<children>
|
||||
<ComboBox fx:id="sourceGroupCombo" editable="true" onAction="#onSourceGroupAction" onHiding="#onSourceGroupHiding" onInputMethodTextChanged="#onSourceGroupTextChanged" onShowing="#onSourceGroupShowing" prefHeight="28.0" promptText="Source group" visibleRowCount="40" />
|
||||
<ComboBox fx:id="destGroupCombo" editable="true" onAction="#onDestGroupAction" onHiding="#onDestGroupHiding" onInputMethodTextChanged="#onDestGroupTextChanged" onShowing="#onDestGroupShowing" prefHeight="28.0" promptText="Destination group" visibleRowCount="40" VBox.vgrow="ALWAYS" />
|
||||
<ChoiceBox fx:id="sourceGroupCombo" minWidth="200.0" prefHeight="28.0" />
|
||||
<ChoiceBox fx:id="destGroupCombo" minWidth="200.0" prefHeight="28.0" />
|
||||
<Button fx:id="transferBtn" mnemonicParsing="false" onAction="#onDoTransfer" text="Transfer" />
|
||||
</children>
|
||||
<padding>
|
||||
|
@ -127,7 +127,7 @@
|
|||
</Text>
|
||||
</top>
|
||||
<center>
|
||||
<ListView fx:id="log" BorderPane.alignment="CENTER" style="-fx-font-size: 8"/>
|
||||
<ListView fx:id="log" style="-fx-font-size: 8" BorderPane.alignment="CENTER" />
|
||||
</center>
|
||||
</BorderPane>
|
||||
</items>
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 4fe39ae7d145b4164c8c4fb4536feac2b2694915
|
||||
Subproject commit 85bac8670d201922ac695f3f086eb0c6cd526e8f
|
Loading…
Reference in New Issue