addon-challenges/src/main/java/world/bentobox/challenges/panel/admin/LibraryPanel.java

474 lines
15 KiB
Java

package world.bentobox.challenges.panel.admin;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.inventory.InventoryCloseEvent;
import org.bukkit.scheduler.BukkitTask;
import lv.id.bonne.panelutils.PanelUtils;
import world.bentobox.bentobox.api.panels.PanelItem;
import world.bentobox.bentobox.api.panels.PanelListener;
import world.bentobox.bentobox.api.panels.builders.PanelBuilder;
import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.challenges.panel.CommonPagedPanel;
import world.bentobox.challenges.panel.CommonPanel;
import world.bentobox.challenges.panel.ConversationUtils;
import world.bentobox.challenges.utils.Constants;
import world.bentobox.challenges.utils.Utils;
import world.bentobox.challenges.web.object.LibraryEntry;
/**
* This class contains all necessary elements to create GUI that lists all challenges.
* It allows to edit them or remove, depending on given input mode.
*/
public class LibraryPanel extends CommonPagedPanel<LibraryEntry>
{
// ---------------------------------------------------------------------
// Section: Constructor
// ---------------------------------------------------------------------
/**
* @param parentGUI ParentGUI object.
*/
private LibraryPanel(CommonPanel parentGUI, Library mode)
{
super(parentGUI);
this.mode = mode;
this.libraryEntries = switch (mode)
{
case WEB -> this.addon.getWebManager().getLibraryEntries();
case DATABASE -> this.generateDatabaseEntries();
case TEMPLATE -> this.generateTemplateEntries();
};
this.filterElements = this.libraryEntries;
}
/**
* This static method allows to easier open Library GUI.
* @param parentGui ParentGUI object.
* @param mode Library view mode.
*/
public static void open(CommonPanel parentGui, Library mode)
{
new LibraryPanel(parentGui, mode).build();
}
// ---------------------------------------------------------------------
// Section: Data Collectors
// ---------------------------------------------------------------------
/**
* This method generates list of database file entries.
*
* @return List of entries for database files.
*/
private List<LibraryEntry> generateDatabaseEntries()
{
File localeDir = this.addon.getDataFolder();
File[] files = localeDir.listFiles(pathname ->
pathname.getName().endsWith(".json") && pathname.isFile());
if (files == null || files.length == 0)
{
// No
return Collections.emptyList();
}
return Arrays.stream(files).
map(file -> LibraryEntry.fromTemplate(
file.getName().substring(0, file.getName().length() - 5),
Material.PAPER)).
collect(Collectors.toList());
}
/**
* This method generates list of template file entries.
*
* @return List of entries for template files.
*/
private List<LibraryEntry> generateTemplateEntries()
{
File localeDir = this.addon.getDataFolder();
File[] files = localeDir.listFiles(pathname ->
pathname.getName().endsWith(".yml") &&
pathname.isFile() &&
!pathname.getName().equals("config.yml"));
if (files == null || files.length == 0)
{
// No
return Collections.emptyList();
}
return Arrays.stream(files).
map(file -> LibraryEntry.fromTemplate(
file.getName().substring(0, file.getName().length() - 4),
Material.PAPER)).
collect(Collectors.toList());
}
// ---------------------------------------------------------------------
// Section: Methods
// ---------------------------------------------------------------------
/**
* This method is called when filter value is updated.
*/
@Override
protected void updateFilters()
{
if (this.searchString == null || this.searchString.isBlank())
{
this.filterElements = this.libraryEntries;
}
else
{
this.filterElements = this.libraryEntries.stream().
filter(element -> {
// If element name is set and name contains search field, then do not filter out.
return element.name().toLowerCase().contains(this.searchString.toLowerCase()) ||
element.author().toLowerCase().contains(this.searchString.toLowerCase()) ||
element.gameMode().toLowerCase().contains(this.searchString.toLowerCase()) ||
element.language().toLowerCase().contains(this.searchString.toLowerCase());
}).
distinct().
collect(Collectors.toList());
}
}
/**
* {@inheritDoc}
*/
@Override
protected void build()
{
if (this.libraryEntries.isEmpty())
{
Utils.sendMessage(this.user,
this.world,
Constants.ERRORS + "no-library-entries");
return;
}
// No point to display. Single element.
if (this.libraryEntries.size() == 1 && !this.mode.equals(Library.WEB))
{
this.generateConfirmationInput(this.libraryEntries.get(0));
return;
}
PanelBuilder panelBuilder = new PanelBuilder().user(this.user).name(
this.user.getTranslation(Constants.TITLE + "library"));
PanelUtils.fillBorder(panelBuilder);
this.populateElements(panelBuilder, this.filterElements);
if (this.mode == Library.WEB)
{
panelBuilder.item(4, this.createDownloadNow());
}
panelBuilder.item(44, this.returnButton);
panelBuilder.listener(new DownloadCanceller());
panelBuilder.build();
}
/**
* This creates download now button, that can skip waiting for automatic request.
* @return PanelItem button that allows to manually download libraries.
*/
private PanelItem createDownloadNow()
{
final String reference = Constants.BUTTON + "download.";
final List<String> description = new ArrayList<>(3);
description.add(this.user.getTranslation(reference + "description"));
description.add(this.user.getTranslation(reference +
(this.clearCache ? "enabled" : "disabled")));
description.add("");
description.add(this.user.getTranslation(Constants.TIPS + "left-click-to-download"));
description.add(this.user.getTranslation(Constants.TIPS + "right-click-to-toggle"));
PanelItemBuilder itemBuilder = new PanelItemBuilder().
name(this.user.getTranslation(reference + "name")).
description(description).
icon(Material.COBWEB).
glow(this.clearCache);
itemBuilder.clickHandler((panel, user1, clickType, slot) ->
{
if (clickType.isRightClick())
{
this.clearCache = !this.clearCache;
panel.getInventory().setItem(slot, this.createDownloadNow().getItem());
}
else
{
this.addon.getWebManager().requestCatalogGitHubData(this.clearCache);
// Fix multiclick issue.
if (this.updateTask != null)
{
this.updateTask.cancel();
}
// add some delay to rebuilding gui.
this.updateTask = this.addon.getPlugin().getServer().getScheduler().runTaskLater(
this.addon.getPlugin(),
this::build,
100L);
}
return true;
});
return itemBuilder.build();
}
/**
* This method creates button for given library entry.
* @param libraryEntry LibraryEntry which button must be created.
* @return Entry button.
*/
@Override
protected PanelItem createElementButton(LibraryEntry libraryEntry)
{
PanelItemBuilder itemBuilder = new PanelItemBuilder().
name(ChatColor.translateAlternateColorCodes('&', libraryEntry.name())).
description(this.generateEntryDescription(libraryEntry)).
description("").
description(this.user.getTranslation(Constants.TIPS + "click-to-install")).
icon(libraryEntry.icon()).
glow(false);
itemBuilder.clickHandler((panel, user1, clickType, i) -> {
this.generateConfirmationInput(libraryEntry);
return true;
});
return itemBuilder.build();
}
/**
* This method generates consumer and calls ConversationAPI for confirmation that processes file downloading,
* importing and gui opening or closing.
*
* @param libraryEntry Entry that must be processed.
*/
private void generateConfirmationInput(LibraryEntry libraryEntry)
{
Consumer<Boolean> consumer = value ->
{
if (value)
{
switch (this.mode)
{
case TEMPLATE -> {
this.addon.getImportManager().importFile(this.user,
this.world,
libraryEntry.name());
CommonPanel.reopen(this.parentPanel != null ? this.parentPanel : this);
}
case DATABASE -> {
this.addon.getImportManager().importDatabaseFile(this.user,
this.world,
libraryEntry.name());
CommonPanel.reopen(this.parentPanel != null ? this.parentPanel : this);
}
case WEB -> {
if (!this.blockedForDownland)
{
this.blockedForDownland = true;
Utils.sendMessage(this.user,
this.world,
Constants.MESSAGES + "start-downloading");
// Run download task after 5 ticks.
this.updateTask = this.addon.getPlugin().getServer().getScheduler().
runTaskLaterAsynchronously(
this.addon.getPlugin(),
() -> this.addon.getWebManager().requestEntryGitHubData(this.user,
this.world,
libraryEntry),
5L);
}
CommonPanel.reopen(this.parentPanel != null ? this.parentPanel : this);
}
}
}
if (this.mode.equals(Library.WEB) || this.libraryEntries.size() > 1)
{
this.build();
}
};
ConversationUtils.createConfirmation(
consumer,
this.user,
this.user.getTranslation(Constants.CONVERSATIONS + "confirm-data-replacement",
Constants.PARAMETER_GAMEMODE, Utils.getGameMode(this.world)),
this.user.getTranslation(Constants.CONVERSATIONS + "new-challenges-imported",
Constants.PARAMETER_GAMEMODE, Utils.getGameMode(this.world)));
}
/**
* This method generated description for LibraryEntry object.
* @param entry LibraryEntry object which description must be generated.
* @return List of strings that will be placed in ItemStack lore message.
*/
private List<String> generateEntryDescription(LibraryEntry entry)
{
final String reference = Constants.DESCRIPTIONS + "library.";
List<String> description = new ArrayList<>();
description.add(this.user.getTranslation(reference + "author",
"[author]", entry.author()));
description.add(entry.description());
description.add(this.user.getTranslation(reference + "gamemode",
"[gamemode]", entry.gameMode()));
description.add(this.user.getTranslation(reference + "lang",
"[lang]", entry.language()));
description.add(this.user.getTranslation(reference + "version",
"[version]", entry.version()));
return description;
}
/**
* This class allows changing icon for Generator Tier
*/
private class DownloadCanceller implements PanelListener
{
/**
* On inventory click.
*
* @param user the user
* @param event the event
*/
@Override
public void onInventoryClick(User user, InventoryClickEvent event)
{
// do nothing
}
/**
* On inventory close.
*
* @param event the event
*/
@Override
public void onInventoryClose(InventoryCloseEvent event)
{
if (LibraryPanel.this.updateTask != null)
{
LibraryPanel.this.updateTask.cancel();
}
}
/**
* Setup current listener.
*/
@Override
public void setup()
{
// Do nothing
}
}
/**
* Enum that holds different view modes for current panel.
*/
public enum Library
{
/**
* Mode for templates available in main folder.
*/
TEMPLATE,
/**
* Mode for database files available in main folder.
*/
DATABASE,
/**
* Mode for web library.
*/
WEB
}
// ---------------------------------------------------------------------
// Section: Instance Variables
// ---------------------------------------------------------------------
/**
* Indicates if download now button should trigger cache clearing.
*/
private boolean clearCache;
/**
* Stores update task that is triggered.
*/
private BukkitTask updateTask = null;
/**
* This variable will protect against spam-click.
*/
private boolean blockedForDownland;
/**
* Stores active library that must be searched.
*/
private final Library mode;
/**
* List of library elements.
*/
private final List<LibraryEntry> libraryEntries;
/**
* Stores filtered items.
*/
private List<LibraryEntry> filterElements;
}