Skip to content

Commit

Permalink
Implement Plugin Endorsements
Browse files Browse the repository at this point in the history
  • Loading branch information
Kouzukii committed Sep 18, 2023
1 parent 5a3196e commit dcef28e
Show file tree
Hide file tree
Showing 6 changed files with 157 additions and 19 deletions.
5 changes: 5 additions & 0 deletions Dalamud/Configuration/Internal/DalamudConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,11 @@ public string EffectiveLanguage
/// </summary>
public double UiBuilderHitch { get; set; } = 100;

/// <summary>
/// Gets or sets a list of endorsed plugins.
/// </summary>
public List<string> EndorsedPluginInternalNames { get; set; } = new();

/// <summary>
/// Load a configuration from the provided path.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Numerics;
using System.Threading;
using System.Threading.Tasks;
Expand Down Expand Up @@ -182,6 +184,7 @@ private enum PluginSortKind
{
Alphabetical,
DownloadCount,
EndorsementCount,
LastUpdate,
NewOrNot,
NotInstalled,
Expand Down Expand Up @@ -504,6 +507,7 @@ private void DrawHeader()
{
(Locs.SortBy_Alphabetical, PluginSortKind.Alphabetical),
(Locs.SortBy_DownloadCounts, PluginSortKind.DownloadCount),
(Locs.SortBy_EndorsementCounts, PluginSortKind.EndorsementCount),
(Locs.SortBy_LastUpdate, PluginSortKind.LastUpdate),
(Locs.SortBy_NewOrNot, PluginSortKind.NewOrNot),
(Locs.SortBy_NotInstalled, PluginSortKind.NotInstalled),
Expand Down Expand Up @@ -1400,11 +1404,13 @@ private void DrawImageTester()
// Name
ImGui.Text("My Cool Plugin");

// Download count
var downloadCountText = Locs.PluginBody_AuthorWithDownloadCount("Plugin Enjoyer", 69420);
// Author, Downloads, Endorsements
var authorText = Locs.PluginBody_Author("Plugin Enjoyer");
var downloadCountText = Locs.PluginBody_DownloadCount(69420);
var endorsementCountText = Locs.PluginBody_EndorsementCount(1337);

ImGui.SameLine();
ImGui.TextColored(ImGuiColors.DalamudGrey3, downloadCountText);
ImGui.TextColored(ImGuiColors.DalamudGrey3, authorText + downloadCountText + endorsementCountText);

cursor.Y += ImGui.GetTextLineHeightWithSpacing();
ImGui.SetCursorPos(cursor);
Expand Down Expand Up @@ -1719,13 +1725,16 @@ private bool DrawPluginCollapsingHeader(string label, LocalPlugin? plugin, IPlug
// Name
ImGui.TextUnformatted(label);

// Download count
var downloadCountText = manifest.DownloadCount > 0
? Locs.PluginBody_AuthorWithDownloadCount(manifest.Author, manifest.DownloadCount)
: Locs.PluginBody_AuthorWithDownloadCountUnavailable(manifest.Author);
// Author, Downloads, Endorsements
var authorText = Locs.PluginBody_Author(manifest.Author);
if (manifest.DownloadCount > 0)
authorText += Locs.PluginBody_DownloadCount(manifest.DownloadCount);

if (manifest.EndorsementCount > 0)
authorText += Locs.PluginBody_EndorsementCount(manifest.EndorsementCount);

ImGui.SameLine();
ImGui.TextColored(ImGuiColors.DalamudGrey3, downloadCountText);
ImGui.TextColored(ImGuiColors.DalamudGrey3, authorText);

if (isNew)
{
Expand Down Expand Up @@ -1857,7 +1866,7 @@ private void DrawChangelog(IChangelogEntry log)
if (log.Author != null)
{
ImGui.SameLine();
ImGui.TextColored(ImGuiColors.DalamudGrey3, Locs.PluginBody_AuthorWithoutDownloadCount(log.Author));
ImGui.TextColored(ImGuiColors.DalamudGrey3, Locs.PluginBody_Author(log.Author));
}

cursor.Y += ImGui.GetTextLineHeightWithSpacing();
Expand Down Expand Up @@ -2175,14 +2184,15 @@ private void DrawInstalledPlugin(LocalPlugin plugin, int index, bool showInstall
ImGui.TextUnformatted(manifest.Name);

// Download count
var downloadText = plugin.IsDev
? Locs.PluginBody_AuthorWithoutDownloadCount(manifest.Author)
: manifest.DownloadCount > 0
? Locs.PluginBody_AuthorWithDownloadCount(manifest.Author, manifest.DownloadCount)
: Locs.PluginBody_AuthorWithDownloadCountUnavailable(manifest.Author);
var authorText = Locs.PluginBody_Author(manifest.Author);
if (manifest.DownloadCount > 0)
authorText += Locs.PluginBody_DownloadCount(manifest.DownloadCount);

if (manifest.EndorsementCount > 0)
authorText += Locs.PluginBody_EndorsementCount(manifest.EndorsementCount);

ImGui.SameLine();
ImGui.TextColored(ImGuiColors.DalamudGrey3, downloadText);
ImGui.TextColored(ImGuiColors.DalamudGrey3, authorText);

var acceptsFeedback =
this.pluginListAvailable.Any(x => x.InternalName == plugin.InternalName && x.AcceptsFeedback);
Expand Down Expand Up @@ -2248,6 +2258,7 @@ private void DrawInstalledPlugin(LocalPlugin plugin, int index, bool showInstall
this.DrawDevPluginButtons(plugin);
this.DrawVisitRepoUrlButton(plugin.Manifest.RepoUrl, false);
this.DrawDeletePluginButton(plugin);
this.DrawEndorsePluginButton(plugin);

if (canFeedback)
{
Expand Down Expand Up @@ -2864,6 +2875,52 @@ private void DrawDeletePluginButton(LocalPlugin plugin)
}
}

private void DrawEndorsePluginButton(LocalPlugin plugin)
{
var showButton = plugin.IsLoaded && !plugin.IsThirdParty && !plugin.IsDev;

if (!showButton)
return;

ImGui.SameLine();
if (plugin.Endorsed)
{
ImGui.PushFont(InterfaceManager.IconFont);
ImGuiComponents.DisabledButton(FontAwesomeIcon.ThumbsUp.ToIconString());
ImGui.PopFont();

if (ImGui.IsItemHovered())
{
ImGui.SetTooltip(Locs.PluginButtonToolTip_PluginAlreadyEndorsed);
}
}
else
{
if (ImGuiComponents.IconButton(FontAwesomeIcon.ThumbsUp))
{
var notifications = Service<NotificationManager>.Get();
plugin.EndorsePluginAsync()
.ContinueWith(task =>
{
if (task.Exception != null)
{
Log.Error(task.Exception, $"An exception occurred while trying to endorse {plugin.Name}");
var message = Locs.Notifications_EndorsementFailed;
if (task.Exception.InnerExceptions.Any(e => e is HttpRequestException { StatusCode: HttpStatusCode.TooManyRequests }))
message = Locs.Notifications_TooManyEndorsements;
notifications.AddNotification(message, Locs.Notifications_EndorsementFailedTitle(plugin.Name), NotificationType.Error);
}
});
}

if (ImGui.IsItemHovered())
{
ImGui.SetTooltip(Locs.PluginButtonToolTip_EndorsePlugin);
}
}
}

private void DrawVisitRepoUrlButton(string? repoUrl, bool big)
{
if (!string.IsNullOrEmpty(repoUrl) && repoUrl.StartsWith("https://"))
Expand Down Expand Up @@ -3042,6 +3099,10 @@ private void ResortPlugins()
this.pluginListAvailable.Sort((p1, p2) => p2.DownloadCount.CompareTo(p1.DownloadCount));
this.pluginListInstalled.Sort((p1, p2) => p2.Manifest.DownloadCount.CompareTo(p1.Manifest.DownloadCount));
break;
case PluginSortKind.EndorsementCount:
this.pluginListAvailable.Sort((p1, p2) => p2.EndorsementCount.CompareTo(p1.EndorsementCount));
this.pluginListInstalled.Sort((p1, p2) => p2.Manifest.EndorsementCount.CompareTo(p1.Manifest.EndorsementCount));
break;
case PluginSortKind.LastUpdate:
this.pluginListAvailable.Sort((p1, p2) => p2.LastUpdate.CompareTo(p1.LastUpdate));
this.pluginListInstalled.Sort((p1, p2) =>
Expand Down Expand Up @@ -3155,6 +3216,8 @@ internal static class Locs
public static string SortBy_Alphabetical => Loc.Localize("InstallerAlphabetical", "Alphabetical");

public static string SortBy_DownloadCounts => Loc.Localize("InstallerDownloadCount", "Download Count");

public static string SortBy_EndorsementCounts => Loc.Localize("InstallerEndorsementCount", "Endorsement Count");

public static string SortBy_LastUpdate => Loc.Localize("InstallerLastUpdate", "Last Update");

Expand Down Expand Up @@ -3250,11 +3313,11 @@ internal static class Locs

#region Plugin body

public static string PluginBody_AuthorWithoutDownloadCount(string author) => Loc.Localize("InstallerAuthorWithoutDownloadCount", " by {0}").Format(author);
public static string PluginBody_Author(string author) => Loc.Localize("InstallerHeaderAuthor", " by {0}").Format(author);

public static string PluginBody_AuthorWithDownloadCount(string author, long count) => Loc.Localize("InstallerAuthorWithDownloadCount", " by {0} ({1} downloads)").Format(author, count.ToString("N0"));
public static string PluginBody_DownloadCount(long count) => Loc.Localize("InstallerHeaderDownloadCount", ", {0} downloads").Format(count.ToString("N0"));

public static string PluginBody_AuthorWithDownloadCountUnavailable(string author) => Loc.Localize("InstallerAuthorWithDownloadCountUnavailable", " by {0}").Format(author);
public static string PluginBody_EndorsementCount(long count) => Loc.Localize("InstallerHeaderEndorsementCount", ", {0} endorsements").Format(count.ToString("N0"));

public static string PluginBody_CurrentChangeLog(Version version) => Loc.Localize("InstallerCurrentChangeLog", "Changelog (v{0})").Format(version);

Expand Down Expand Up @@ -3339,6 +3402,10 @@ public static string PluginBody_BannedReason(string message) =>

public static string PluginButtonToolTip_SingleProfileDisabled(string name) => Loc.Localize("InstallerSingleProfileDisabled", "The collection '{0}' which contains this plugin is disabled.\nPlease enable it in the collections manager to toggle the plugin individually.").Format(name);

public static string PluginButtonToolTip_EndorsePlugin => Loc.Localize("InstallerEndorsePlugin", "Show your appreciation of this plugin by endorsing it");

public static string PluginButtonToolTip_PluginAlreadyEndorsed => Loc.Localize("InstallerPluginAlreadyEndorsed", "You have already endorsed this plugin");

#endregion

#region Notifications
Expand Down Expand Up @@ -3367,6 +3434,12 @@ public static string PluginBody_BannedReason(string message) =>

public static string Notifications_PluginEnabled(string name) => Loc.Localize("NotificationsPluginEnabled", "'{0}' was enabled.").Format(name);

public static string Notifications_EndorsementFailedTitle(string name) => Loc.Localize("NotificationsPluginEndorsementFailedTitle", "Failed to endorse '{0}'!").Format(name);

public static string Notifications_EndorsementFailed => Loc.Localize("NotificationsPluginEndorsementFailed", "Please try again later.");

public static string Notifications_TooManyEndorsements => Loc.Localize("NotificationsPluginTooManyEndorsements", "You have sent too many endorsement requests.");

#endregion

#region Footer
Expand Down
31 changes: 30 additions & 1 deletion Dalamud/Plugin/Internal/PluginManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;

using System.Web;
using CheapLoc;
using Dalamud.Configuration;
using Dalamud.Configuration.Internal;
Expand Down Expand Up @@ -1435,6 +1435,35 @@ private async Task<LocalPlugin> LoadPluginAsync(FileInfo dllFile, LocalPluginMan
return plugin;
}

/// <summary>
/// Determine if a plugin has been endorsed already.
/// </summary>
/// <param name="plugin">The local plugin.</param>
/// <returns>A value indicating whether the plugin has been endorsed already.</returns>
public bool IsPluginEndorsed(LocalPlugin plugin)
{
var config = Service<DalamudConfiguration>.Get();

return config.EndorsedPluginInternalNames.Contains(plugin.InternalName);
}

/// <summary>
/// Endorse a plugin.
/// </summary>
/// <param name="localPlugin">The plugin to endorse.</param>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public async Task EndorsePluginAsync(LocalPlugin localPlugin)
{
Log.Debug($"Endorsing plugin {localPlugin.Name}");

var response = await this.happyHttpClient.SharedHttpClient.PostAsync("https://kamori.goats.dev/Plugin/Endorse/" + HttpUtility.UrlEncode(localPlugin.InternalName), null);
response.EnsureSuccessStatusCode();

var config = Service<DalamudConfiguration>.Get();
config.EndorsedPluginInternalNames.Add(localPlugin.InternalName);
config.QueueSave();
}

private void DetectAvailablePluginUpdates()
{
lock (this.pluginListLock)
Expand Down
22 changes: 22 additions & 0 deletions Dalamud/Plugin/Internal/Types/LocalPlugin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,11 @@ public LocalPlugin(FileInfo dllFile, LocalPluginManifest? manifest)
/// Gets the effective version of this plugin.
/// </summary>
public Version EffectiveVersion => this.manifest.EffectiveVersion;

/// <summary>
/// Gets a value indicating whether the user has endorsed this plugin.
/// </summary>
public bool Endorsed { get; private set; }

/// <summary>
/// Gets the service scope for this plugin.
Expand Down Expand Up @@ -507,6 +512,8 @@ public async Task LoadAsync(PluginLoadReason reason, bool reloading = false)
this.manifest.Save(this.manifestFile, "manifest name null or empty");
}

this.Endorsed = pluginManager.IsPluginEndorsed(this);

this.State = PluginState.Loaded;
Log.Information($"Finished loading {this.DllFile.Name}");
}
Expand Down Expand Up @@ -686,6 +693,21 @@ public void ReloadManifest()
});
}

public async Task EndorsePluginAsync()
{
this.Endorsed = true;
try
{
var pluginManager = await Service<PluginManager>.GetAsync();
await pluginManager.EndorsePluginAsync(this);
}
catch
{
this.Endorsed = false;
throw;
}
}

private static void SetupLoaderConfig(LoaderConfig config)
{
config.IsUnloadable = true;
Expand Down
5 changes: 5 additions & 0 deletions Dalamud/Plugin/Internal/Types/Manifest/IPluginManifest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,11 @@ public interface IPluginManifest
/// </summary>
public long DownloadCount { get; }

/// <summary>
/// Gets the number of endorsements this plugin has.
/// </summary>
public long EndorsementCount { get; }

/// <summary>
/// Gets a value indicating whether the plugin supports profiles.
/// </summary>
Expand Down
4 changes: 4 additions & 0 deletions Dalamud/Plugin/Internal/Types/PluginManifest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,10 @@ internal record PluginManifest : IPluginManifest
[JsonProperty]
public long DownloadCount { get; init; }

/// <inheritdoc/>
[JsonProperty]
public long EndorsementCount { get; init; }

/// <inheritdoc/>
[JsonProperty]
public long LastUpdate { get; set; }
Expand Down

0 comments on commit dcef28e

Please sign in to comment.