diff --git a/core/src/main/java/com/rappytv/betterfriends/BetterFriendsAddon.java b/core/src/main/java/com/rappytv/betterfriends/BetterFriendsAddon.java index 8298378..ff86bcb 100644 --- a/core/src/main/java/com/rappytv/betterfriends/BetterFriendsAddon.java +++ b/core/src/main/java/com/rappytv/betterfriends/BetterFriendsAddon.java @@ -19,7 +19,9 @@ import com.rappytv.betterfriends.ui.hud.UnreadChatCountWidget; import com.rappytv.betterfriends.ui.tags.FriendNoteNameTag; import com.rappytv.betterfriends.ui.tags.FriendPinIconTag; +import com.rappytv.betterfriends.utils.FriendshipExpirationHandler; import com.rappytv.betterfriends.utils.GroupHelper; +import com.rappytv.betterfriends.utils.VoiceChatHelper; import net.labymod.api.Laby; import net.labymod.api.addon.LabyAddon; import net.labymod.api.client.component.Component; @@ -37,16 +39,20 @@ public class BetterFriendsAddon extends LabyAddon { private final LegacyComponentSerializer serializer = LegacyComponentSerializer.legacyAmpersand(); private static BetterFriendsAddon instance; + private final VoiceChatHelper voiceChatHelper = new VoiceChatHelper(); + private FriendshipExpirationHandler expirationHandler; @Override protected void enable() { instance = this; this.registerSettingCategory(); GroupHelper.registerGroupIds(); + this.expirationHandler = new FriendshipExpirationHandler(this); this.registerCommand(new BetterFriendsCommand()); this.registerListener(new ChatReceiveListener(this)); + this.registerListener(this.expirationHandler); this.registerListener(new FriendAddListener(this)); this.registerListener(new FriendRemoveListener(this)); this.registerListener(new FriendRequestReceiveListener(this)); @@ -103,6 +109,18 @@ public static TextComponent getPrefix() { .append(Component.space()); } + public static BetterFriendsAddon getInstance() { + return instance; + } + + public FriendshipExpirationHandler getExpirationHandler() { + return this.expirationHandler; + } + + public VoiceChatHelper getVoiceChatHelper() { + return this.voiceChatHelper; + } + public LegacyComponentSerializer getSerializer() { return this.serializer; } diff --git a/core/src/main/java/com/rappytv/betterfriends/config/BetterFriendsConfig.java b/core/src/main/java/com/rappytv/betterfriends/config/BetterFriendsConfig.java index f9a06a5..afeebd0 100644 --- a/core/src/main/java/com/rappytv/betterfriends/config/BetterFriendsConfig.java +++ b/core/src/main/java/com/rappytv/betterfriends/config/BetterFriendsConfig.java @@ -1,6 +1,7 @@ package com.rappytv.betterfriends.config; import com.rappytv.betterfriends.config.subconfig.FriendNoteTagConfig; +import com.rappytv.betterfriends.config.subconfig.FriendlistExpiryConfig; import com.rappytv.betterfriends.config.subconfig.PinIconConfig; import com.rappytv.betterfriends.ui.activities.config.FriendlistActivity; import com.rappytv.betterfriends.ui.widgets.FriendlistFriendWidget; @@ -36,6 +37,8 @@ public Activity advancedFriendlist() { return new FriendlistActivity<>(FriendlistFriendWidget::new); } + private final FriendlistExpiryConfig expiryConfig = new FriendlistExpiryConfig(); + @SpriteSlot(size = 8, x = 4) private final PinIconConfig pinIconConfig = new PinIconConfig(); @SpriteSlot(x = 3) @@ -93,6 +96,9 @@ public ConfigProperty enabled() { public ConfigProperty prefixColor() { return this.prefixColor; } + public FriendlistExpiryConfig expiryConfig() { + return this.expiryConfig; + } public PinIconConfig pinIconConfig() { return this.pinIconConfig; } diff --git a/core/src/main/java/com/rappytv/betterfriends/config/subconfig/FriendlistExpiryConfig.java b/core/src/main/java/com/rappytv/betterfriends/config/subconfig/FriendlistExpiryConfig.java new file mode 100644 index 0000000..1a5db3b --- /dev/null +++ b/core/src/main/java/com/rappytv/betterfriends/config/subconfig/FriendlistExpiryConfig.java @@ -0,0 +1,49 @@ +package com.rappytv.betterfriends.config.subconfig; + +import net.labymod.api.client.gui.screen.activity.Activity; +import net.labymod.api.client.gui.screen.widget.widgets.activity.settings.ActivitySettingWidget.ActivitySetting; +import net.labymod.api.client.gui.screen.widget.widgets.input.SwitchWidget.SwitchSetting; +import net.labymod.api.configuration.loader.Config; +import net.labymod.api.configuration.loader.annotation.Exclude; +import net.labymod.api.configuration.loader.property.ConfigProperty; +import net.labymod.api.util.MethodOrder; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +public class FriendlistExpiryConfig extends Config { + + @Exclude + private final Map expirations = new HashMap<>(); + + @SwitchSetting + private final ConfigProperty sendNotifications = new ConfigProperty<>(true); + + @SwitchSetting + private final ConfigProperty notifyOnExpiration = new ConfigProperty<>(true); + + @MethodOrder(after = "notifyOnExpiration") + @ActivitySetting + public Activity manageExpirationDates() { + return null; + } + + public void addExpiration(UUID uuid, long expiration) { + this.expirations.put(uuid, expiration); + } + + public void removeExpiration(UUID uuid) { + this.expirations.remove(uuid); + } + + public Map getExpirations() { + return this.expirations; + } + + public ConfigProperty sendNotifications() { + return this.sendNotifications; + } + public ConfigProperty notifyOnExpiration() { + return this.notifyOnExpiration; + } +} diff --git a/core/src/main/java/com/rappytv/betterfriends/listeners/FriendAddListener.java b/core/src/main/java/com/rappytv/betterfriends/listeners/FriendAddListener.java index aac122b..d37d067 100644 --- a/core/src/main/java/com/rappytv/betterfriends/listeners/FriendAddListener.java +++ b/core/src/main/java/com/rappytv/betterfriends/listeners/FriendAddListener.java @@ -1,12 +1,14 @@ package com.rappytv.betterfriends.listeners; import com.rappytv.betterfriends.BetterFriendsAddon; -import net.labymod.addons.voicechat.api.audio.stream.AudioStreamState; -import net.labymod.addons.voicechat.api.client.VoiceConnector; -import net.labymod.addons.voicechat.core.VoiceChatAddon; +import com.rappytv.betterfriends.ui.activities.FriendlistExpirationActivity; import net.labymod.api.Laby; +import net.labymod.api.client.component.Component; +import net.labymod.api.client.gui.icon.Icon; import net.labymod.api.event.Subscribe; import net.labymod.api.event.labymod.labyconnect.session.friend.LabyConnectFriendAddEvent; +import net.labymod.api.notification.Notification; +import net.labymod.api.notification.Notification.NotificationButton; public class FriendAddListener { @@ -18,28 +20,28 @@ public FriendAddListener(BetterFriendsAddon addon) { @Subscribe public void onFriendAdd(LabyConnectFriendAddEvent event) { - if (!this.addon.configuration().restartWhenMuted().get()) { - return; + if(this.addon.configuration().expiryConfig().sendNotifications().get()) { + Notification.builder() + .title(Component.translatable("betterfriends.notifications.friendshipExpiration.set.title")) + .text(Component.translatable("betterfriends.notifications.friendshipExpiration.set.description")) + .icon(Icon.head(event.friend().getUniqueId(), true)) + .addButton(NotificationButton.of( + Component.translatable("betterfriends.notifications.friendshipExpiration.set.button"), + () -> Laby.labyAPI().minecraft().minecraftWindow().displayScreen( + new FriendlistExpirationActivity(event.friend()) + ) + )) + .duration(20000) + .buildAndPush(); } - if (!Laby.labyAPI().addonService().isEnabled("voicechat")) { + if (!this.addon.configuration().restartWhenMuted().get()) { return; } - - VoiceConnector client = VoiceChatAddon.INSTANCE.client(); - if (!client.isAuthenticated() || !this.isSelfMuted()) { + if (!this.addon.getVoiceChatHelper().isEnabled()) { return; } - client.disconnect(); - client.connect(); + this.addon.getVoiceChatHelper().reconnect(); } - - private boolean isSelfMuted() { - return VoiceChatAddon.INSTANCE - .referenceStorage() - .audioStreamRegistry() - .getState(Laby.labyAPI().getUniqueId(), false) == AudioStreamState.INPUT_GLOBAL_MUTED; - } - } diff --git a/core/src/main/java/com/rappytv/betterfriends/ui/activities/FriendlistExpirationActivity.java b/core/src/main/java/com/rappytv/betterfriends/ui/activities/FriendlistExpirationActivity.java new file mode 100644 index 0000000..5cc6410 --- /dev/null +++ b/core/src/main/java/com/rappytv/betterfriends/ui/activities/FriendlistExpirationActivity.java @@ -0,0 +1,148 @@ +package com.rappytv.betterfriends.ui.activities; + +import com.rappytv.betterfriends.BetterFriendsAddon; +import com.rappytv.betterfriends.utils.FriendshipExpirationHandler; +import com.rappytv.betterfriends.utils.FriendshipExpirationHandler.FriendshipExpirationType; +import com.rappytv.betterfriends.utils.VoiceChatHelper; +import net.labymod.api.Laby; +import net.labymod.api.client.component.Component; +import net.labymod.api.client.component.format.NamedTextColor; +import net.labymod.api.client.gui.icon.Icon; +import net.labymod.api.client.gui.screen.Parent; +import net.labymod.api.client.gui.screen.ScreenInstance; +import net.labymod.api.client.gui.screen.activity.AutoActivity; +import net.labymod.api.client.gui.screen.activity.Link; +import net.labymod.api.client.gui.screen.activity.types.SimpleActivity; +import net.labymod.api.client.gui.screen.widget.Widget; +import net.labymod.api.client.gui.screen.widget.widgets.ComponentWidget; +import net.labymod.api.client.gui.screen.widget.widgets.input.ButtonWidget; +import net.labymod.api.client.gui.screen.widget.widgets.input.TextFieldWidget; +import net.labymod.api.client.gui.screen.widget.widgets.input.dropdown.DropdownWidget; +import net.labymod.api.client.gui.screen.widget.widgets.layout.FlexibleContentWidget; +import net.labymod.api.client.gui.screen.widget.widgets.layout.list.HorizontalListWidget; +import net.labymod.api.client.gui.screen.widget.widgets.layout.list.VerticalListWidget; +import net.labymod.api.client.gui.screen.widget.widgets.renderer.IconWidget; +import net.labymod.api.labyconnect.protocol.model.friend.Friend; +import net.labymod.api.util.I18n; + +@Link("expiration.lss") +@AutoActivity +public class FriendlistExpirationActivity extends SimpleActivity { + + private final FriendshipExpirationHandler handler; + private final VoiceChatHelper voiceChatHelper; + private final Friend friend; + private TextFieldWidget customExpirationField; + + public FriendlistExpirationActivity(Friend friend) { + this.handler = BetterFriendsAddon.getInstance().getExpirationHandler(); + this.voiceChatHelper = BetterFriendsAddon.getInstance().getVoiceChatHelper(); + this.friend = friend; + } + + @Override + public void initialize(Parent parent) { + super.initialize(parent); + + FlexibleContentWidget window = new FlexibleContentWidget() + .addId("window"); + + HorizontalListWidget profileWrapper = new HorizontalListWidget() + .addId("header"); + IconWidget headIcon = new IconWidget(Icon.head(this.friend.getUniqueId())).addId("head"); + ComponentWidget nameComponent = ComponentWidget.i18n( + "betterfriends.settings.advancedFriendlist.expiration.title", + this.friend.getName() + ).addId("username"); + VerticalListWidget content = new VerticalListWidget<>().addId("content"); + + profileWrapper.addEntry(headIcon); + profileWrapper.addEntry(nameComponent); + + DropdownWidget expirationTypeDropdown = new DropdownWidget<>(); + if(Laby.labyAPI().minecraft().isIngame()) { + expirationTypeDropdown.add(FriendshipExpirationType.ON_SERVER_LEAVE); + } + if(this.voiceChatHelper.isEnabled()) { + long expiration = this.voiceChatHelper.getMuteExpiration(this.friend.getUniqueId()); + if(expiration != -1) { + expirationTypeDropdown.add(FriendshipExpirationType.ON_MUTE_EXPIRATION); + } + } + expirationTypeDropdown.add(FriendshipExpirationType.CUSTOM_DATE); + expirationTypeDropdown.setSelected(expirationTypeDropdown.entries().getFirst()); + expirationTypeDropdown.setTranslationKeyPrefix("betterfriends.settings.advancedFriendlist.expiration.types"); + + this.customExpirationField = new TextFieldWidget(); + this.customExpirationField.placeholder(Component.text(I18n.translate( + "betterfriends.settings.advancedFriendlist.expiration.forExample" + ) + " 6h, 2d, 4w, 1y")); + this.customExpirationField.setVisible(expirationTypeDropdown.getSelected() == FriendshipExpirationType.CUSTOM_DATE); + + ButtonWidget saveButton = ButtonWidget.i18n( + "betterfriends.settings.advancedFriendlist.expiration.button", () -> { + switch (expirationTypeDropdown.getSelected()) { + case ON_SERVER_LEAVE -> + this.handler.scheduleServerLeaveExpiration(this.friend.getUniqueId()); + case ON_MUTE_EXPIRATION -> + this.handler.scheduleVoiceUnmuteExpiration(this.friend.getUniqueId()); + case CUSTOM_DATE -> { + long duration = getDuration(this.customExpirationField.getText()); + this.handler.scheduleCustomExpiration( + this.friend.getUniqueId(), + System.currentTimeMillis() + duration + ); + } + } + Laby.references().chatExecutor().displayClientMessage( + Component.empty() + .append(BetterFriendsAddon.getPrefix()) + .append(Component.translatable( + "betterfriends.settings.advancedFriendlist.expiration.success", + NamedTextColor.GREEN + )) + ); + Laby.labyAPI().minecraft().minecraftWindow().displayScreen((ScreenInstance) null); + }); + Runnable updateButtonVisibility = () -> saveButton.setEnabled( + expirationTypeDropdown.getSelected() != FriendshipExpirationType.CUSTOM_DATE + || getDuration(this.customExpirationField.getText()) > 0 + ); + expirationTypeDropdown.setChangeListener((type) -> { + this.customExpirationField.setVisible(type == FriendshipExpirationType.CUSTOM_DATE); + updateButtonVisibility.run(); + }); + this.customExpirationField.updateListener((text) -> updateButtonVisibility.run()); + updateButtonVisibility.run(); + + window.addContent(profileWrapper); + window.addContent(content); + + content.addChild(expirationTypeDropdown); + content.addChild(this.customExpirationField); + content.addChild(saveButton); + + this.document.addChild(window); + } + + public static long getDuration(String timeArg) { + String format; + long duration; + try { + format = timeArg.substring(timeArg.length() - 1); + duration = Integer.parseInt(timeArg.substring(0, timeArg.length() - 1)); + } catch (NumberFormatException | IndexOutOfBoundsException e) { + return -1; + } + + return switch (format) { + case "s" -> duration * 1000; + case "m" -> duration * 1000 * 60; + case "h" -> duration * 1000 * 60 * 60; + case "d" -> duration * 1000 * 60 * 60 * 24; + case "w" -> duration * 1000 * 60 * 60 * 24 * 7; + case "y" -> duration * 1000 * 60 * 60 * 24 * 7 * 52; + default -> -1; + }; + } +} diff --git a/core/src/main/java/com/rappytv/betterfriends/ui/activities/config/FriendlistActivity.java b/core/src/main/java/com/rappytv/betterfriends/ui/activities/config/FriendlistActivity.java index 131c123..34a9764 100644 --- a/core/src/main/java/com/rappytv/betterfriends/ui/activities/config/FriendlistActivity.java +++ b/core/src/main/java/com/rappytv/betterfriends/ui/activities/config/FriendlistActivity.java @@ -21,8 +21,11 @@ import net.labymod.api.client.gui.screen.widget.widgets.layout.list.VerticalListWidget; import net.labymod.api.event.Phase; import net.labymod.api.event.Subscribe; +import net.labymod.api.event.labymod.labyconnect.LabyConnectStateUpdateEvent; import net.labymod.api.event.labymod.labyconnect.session.friend.LabyConnectFriendAddEvent; +import net.labymod.api.event.labymod.labyconnect.session.friend.LabyConnectFriendPinUpdateEvent; import net.labymod.api.event.labymod.labyconnect.session.friend.LabyConnectFriendRemoveEvent; +import net.labymod.api.event.labymod.labyconnect.session.friend.LabyConnectFriendStatusEvent; import net.labymod.api.event.labymod.labyconnect.session.login.LabyConnectFriendAddBulkEvent; import net.labymod.api.event.labymod.user.UserUpdateDataEvent; import net.labymod.api.labyconnect.LabyConnectSession; @@ -202,6 +205,27 @@ public void onLabyConnectFriendRemove(LabyConnectFriendRemoveEvent event) { ); } + @Subscribe + public void onLabyConnectStateUpdate(LabyConnectStateUpdateEvent event) { + this.initializeFriendlist(true); + } + + @Subscribe + public void onLabyConnectStateUpdate(LabyConnectFriendPinUpdateEvent event) { + this.entries.reInitializeChildrenIf( + FriendWidget.class, + widget -> widget.getFriend().getUniqueId().equals(event.friend().getUniqueId()) + ); + } + + @Subscribe + public void onLabyConnectFriendStatus(LabyConnectFriendStatusEvent event) { + this.entries.reInitializeChildrenIf( + FriendWidget.class, + widget -> widget.getFriend().getUniqueId().equals(event.friend().getUniqueId()) + ); + } + private enum SortingStrategy { ONLINE_STATUS((a, b) -> Boolean.compare(b.isOnline(), a.isOnline())), ROLE((a, b) -> Integer.compare( diff --git a/core/src/main/java/com/rappytv/betterfriends/ui/widgets/FriendlistFriendWidget.java b/core/src/main/java/com/rappytv/betterfriends/ui/widgets/FriendlistFriendWidget.java index 649e95c..91b9c69 100644 --- a/core/src/main/java/com/rappytv/betterfriends/ui/widgets/FriendlistFriendWidget.java +++ b/core/src/main/java/com/rappytv/betterfriends/ui/widgets/FriendlistFriendWidget.java @@ -1,6 +1,7 @@ package com.rappytv.betterfriends.ui.widgets; import java.util.List; +import com.rappytv.betterfriends.ui.activities.FriendlistExpirationActivity; import net.labymod.api.Laby; import net.labymod.api.Textures; import net.labymod.api.Textures.SpriteCommon; @@ -31,7 +32,6 @@ public List getButtons() { ButtonWidget removeConfirmationButton = ButtonWidget.icon(SpriteCommon.GREEN_CHECKED, () -> { this.friend.remove(); this.confirmRemoval = false; - this.reInitialize(); }); removeConfirmationButton.setHoverComponent(Component.translatable( "betterfriends.settings.advancedFriendlist.removal.confirm", @@ -53,7 +53,6 @@ public List getButtons() { } else { this.friend.pin(); } - this.reInitialize(); }).addId("pin-button"); pinButton.setHoverComponent( Component.translatable( @@ -69,6 +68,15 @@ public List getButtons() { noteButton.setHoverComponent(Component.translatable( "labymod.activity.labyconnect.chat.action.note" )); + ButtonWidget expirationButton = ButtonWidget.icon( + SpriteCommon.QUESTION_MARK, // TODO: Add real icon + () -> Laby.labyAPI().minecraft().minecraftWindow().displayScreen( + new FriendlistExpirationActivity(this.friend) + ) + ).addId("expiration-button"); + expirationButton.setHoverComponent(Component.translatable( + "betterfriends.settings.advancedFriendlist.expiration.label" + )); ButtonWidget removeButton = ButtonWidget.component(X, () -> { if (this.skipConfirmation) { this.friend.remove(); @@ -89,7 +97,7 @@ public List getButtons() { NamedTextColor.DARK_GRAY )) ); - return List.of(pinButton, noteButton, removeButton); + return List.of(pinButton, noteButton, expirationButton, removeButton); } @Subscribe diff --git a/core/src/main/java/com/rappytv/betterfriends/utils/FriendshipExpirationHandler.java b/core/src/main/java/com/rappytv/betterfriends/utils/FriendshipExpirationHandler.java new file mode 100644 index 0000000..aec8b3e --- /dev/null +++ b/core/src/main/java/com/rappytv/betterfriends/utils/FriendshipExpirationHandler.java @@ -0,0 +1,139 @@ +package com.rappytv.betterfriends.utils; + +import com.rappytv.betterfriends.BetterFriendsAddon; +import com.rappytv.betterfriends.config.subconfig.FriendlistExpiryConfig; +import net.labymod.api.Laby; +import net.labymod.api.client.component.Component; +import net.labymod.api.client.component.format.NamedTextColor; +import net.labymod.api.client.gui.icon.Icon; +import net.labymod.api.event.Subscribe; +import net.labymod.api.event.client.network.server.ServerDisconnectEvent; +import net.labymod.api.event.labymod.labyconnect.LabyConnectStateUpdateEvent; +import net.labymod.api.labyconnect.LabyConnectSession; +import net.labymod.api.labyconnect.protocol.LabyConnectState; +import net.labymod.api.notification.Notification; +import net.labymod.api.util.I18n; +import net.labymod.api.util.concurrent.task.Task; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +public class FriendshipExpirationHandler { + + private final Set serverLeaveExpirations = new HashSet<>(); + private final BetterFriendsAddon addon; + private final FriendlistExpiryConfig config; + + public FriendshipExpirationHandler(BetterFriendsAddon addon) { + this.addon = addon; + this.config = addon.configuration().expiryConfig(); + + Task.builder(this::removeExpiredFriendships).repeat(1, TimeUnit.MINUTES).build().execute(); + } + + public void removeExpiredFriendships() { + List toRemove = new ArrayList<>(); + for (UUID uuid : this.config.getExpirations().keySet()) { + if(this.config.getExpirations().get(uuid) > System.currentTimeMillis()) { + continue; + } + if(!this.removeFriend(uuid)) { + continue; + } + toRemove.add(uuid); + if(!this.config.notifyOnExpiration().get()) { + continue; + } + Laby.labyAPI().labyNetController().loadNameByUniqueId(uuid, (name) -> + Notification.builder() + .title(Component.translatable("betterfriends.notifications.friendshipExpiration.expired.title")) + .text(Component.translatable( + "betterfriends.notifications.friendshipExpiration.expired.description", + Component.text( + Objects.requireNonNull(name.getOrDefault(I18n.translate( + "betterfriends.notifications.friendshipExpiration.expired.unknown" + ))), + NamedTextColor.AQUA + ) + )) + .icon(Icon.head(uuid, true)) + .duration(20000) + .buildAndPush()); + } + toRemove.forEach(this.config::removeExpiration); + } + + public void scheduleCustomExpiration(UUID uuid, long expiration) { + this.config.addExpiration(uuid, expiration); + } + + public void scheduleVoiceUnmuteExpiration(UUID uuid) { + long expiration = this.addon.getVoiceChatHelper().getMuteExpiration(uuid); + if(expiration != -1) { + this.config.addExpiration(uuid, expiration); + } + } + + public void scheduleServerLeaveExpiration(UUID uuid) { + this.serverLeaveExpirations.add(uuid); + } + + @Subscribe + public void onLabyChatConnect(LabyConnectStateUpdateEvent event) { + if(event.state() == LabyConnectState.PLAY) { + this.removeExpiredFriendships(); + } + } + + @Subscribe + public void onServerLeave(ServerDisconnectEvent event) { + List toRemove = new ArrayList<>(); + for(UUID uuid : this.serverLeaveExpirations) { + if(!this.removeFriend(uuid)) { + continue; + } + toRemove.add(uuid); + if(!this.config.notifyOnExpiration().get()) { + continue; + } + Laby.labyAPI().labyNetController().loadNameByUniqueId(uuid, (name) -> + Notification.builder() + .title(Component.translatable("betterfriends.notifications.friendshipExpiration.expired.title")) + .text(Component.translatable( + "betterfriends.notifications.friendshipExpiration.expired.disconnectDescription", + Component.text( + Objects.requireNonNull(name.getOrDefault(I18n.translate( + "betterfriends.notifications.friendshipExpiration.expired.unknown" + ))), + NamedTextColor.AQUA + ) + )) + .icon(Icon.head(uuid, true)) + .duration(20000) + .buildAndPush()); + } + toRemove.forEach(this.serverLeaveExpirations::remove); + } + + @SuppressWarnings("all") + private boolean removeFriend(UUID uuid) { + LabyConnectSession session = Laby.labyAPI().labyConnect().getSession(); + if(session == null || !session.isAuthenticated()) { + return false; + } + if(!Laby.labyAPI().labyModLoader().isAddonDevelopmentEnvironment()) { + session.removeFriend(uuid); + } + return true; + } + + public enum FriendshipExpirationType { + ON_SERVER_LEAVE, + ON_MUTE_EXPIRATION, + CUSTOM_DATE + } +} diff --git a/core/src/main/java/com/rappytv/betterfriends/utils/VoiceChatHelper.java b/core/src/main/java/com/rappytv/betterfriends/utils/VoiceChatHelper.java new file mode 100644 index 0000000..47e603c --- /dev/null +++ b/core/src/main/java/com/rappytv/betterfriends/utils/VoiceChatHelper.java @@ -0,0 +1,46 @@ +package com.rappytv.betterfriends.utils; + +import net.labymod.addons.voicechat.api.audio.stream.AudioStreamState; +import net.labymod.addons.voicechat.api.client.VoiceConnector; +import net.labymod.addons.voicechat.api.client.user.VoiceUser; +import net.labymod.addons.voicechat.core.VoiceChatAddon; +import net.labymod.api.Laby; +import javax.inject.Singleton; +import java.util.UUID; + +@Singleton +public class VoiceChatHelper { + + public boolean isEnabled() { + return Laby.labyAPI().addonService().isEnabled("voicechat"); + } + + public void reconnect() { + VoiceConnector client = VoiceChatAddon.INSTANCE.client(); + if (!client.isAuthenticated() || !this.isSelfMuted()) { + return; + } + + client.disconnect(); + client.connect(); + } + + public long getMuteExpiration(UUID uuid) { + VoiceUser user = VoiceChatAddon.INSTANCE + .referenceStorage() + .voiceUserRegistry().get(uuid); + + if (user == null) { + return -1; + } + + return user.isMuted() ? user.getMute().getTimeEnd() : -1; + } + + private boolean isSelfMuted() { + return VoiceChatAddon.INSTANCE + .referenceStorage() + .audioStreamRegistry() + .getClientState(false) == AudioStreamState.INPUT_GLOBAL_MUTED; + } +} diff --git a/core/src/main/resources/assets/betterfriends/i18n/en_us.json b/core/src/main/resources/assets/betterfriends/i18n/en_us.json index 7561f2e..1e55dc4 100644 --- a/core/src/main/resources/assets/betterfriends/i18n/en_us.json +++ b/core/src/main/resources/assets/betterfriends/i18n/en_us.json @@ -24,17 +24,41 @@ }, "advancedFriendlist": { "name": "Advanced friend list", + "sorting": { + "onlineStatus": "Online status", + "role": "LabyMod role", + "aToZ": "A-Z", + "zToA": "Z-A" + }, + "expiration": { + "label": "Add expiration date", + "title": "Set expiration date for %s", + "forExample": "e. g.", + "button": "Set expiration", + "types": { + "onServerLeave": "On disconnect", + "onMuteExpiration": "When VoiceMute expires", + "customDate": "Custom date" + }, + "success": "The expiration date was successfully set! You can cancel the expiration in the addon settings." + }, "removal": { "label": "Remove friend", "skipConfirmation": "(Hold shift to skip confirmation)", "confirm": "Confirm removal", "cancel": "Cancel removal" + } + }, + "expiryConfig": { + "name": "Temporary friendships", + "sendNotifications": { + "name": "Ask if frienship should be temporary when adding new friend" }, - "sorting": { - "onlineStatus": "Online status", - "role": "LabyMod role", - "aToZ": "A-Z", - "zToA": "Z-A" + "notifyOnExpiration": { + "name": "Notify when temporary friendship expires" + }, + "manageExpirationDates": { + "name": "Manage expiring friendships" } }, "pinIconConfig": { @@ -200,6 +224,19 @@ "title": "Request removed", "description": "%s has declined your friend request. (Or you removed it)" }, + "friendshipExpiration": { + "set": { + "title": "New friend added", + "description": "Do you want to set an expiration date for the friendship?", + "button": "Add expiration date" + }, + "expired": { + "title": "Friendship expired!", + "description": "Your friendship with %s has expired.", + "disconnectDescription": "Your friendship with %s has expired because you left the server.", + "unknown": "Unknown" + } + }, "statusUpdate": { "message": "%1$s is now %2$s!", "offline": "offline" diff --git a/core/src/main/resources/assets/betterfriends/themes/vanilla/lss/expiration.lss b/core/src/main/resources/assets/betterfriends/themes/vanilla/lss/expiration.lss new file mode 100644 index 0000000..daf33cb --- /dev/null +++ b/core/src/main/resources/assets/betterfriends/themes/vanilla/lss/expiration.lss @@ -0,0 +1,51 @@ +.window { + top: 50%; + left: 50%; + width: 300; + height: 180; + alignment-x: center; + alignment-y: center; + + .header { + left: 50%; + width: 100%; + height: 20; + padding: 0 2 0 2; + space-between-entries: 3; + + .head { + width: 16; + height: width; + alignment: center; + } + + .username { + alignment: center; + } + } + + .content { + width: 100%; + height: 160; + alignment-x: center; + alignment-y: center; + overwrite-width: false; + + Dropdown { + width: 112; + margin-top: 20; + alignment-x: center; + } + + TextField { + width: 100; + margin-top: 10; + alignment-x: center; + } + + Button { + margin-top: 10; + alignment-x: center; + } + } +} \ No newline at end of file