Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
650 changes: 522 additions & 128 deletions .github/workflows/build.yml

Large diffs are not rendered by default.

27 changes: 23 additions & 4 deletions .github/workflows/wiki-drawio-upload.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,18 @@ jobs:
name: Update Wiki Documentation
runs-on: ubuntu-latest
steps:
- name: Normalize release tag
id: version
run: |
TAG="${{ inputs.release_tag || 'nightly' }}"

if [[ "$TAG" == "nightly" || "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "release_tag=$TAG" >> "$GITHUB_OUTPUT"
else
echo "Invalid release tag '$TAG', using nightly."
echo "release_tag=nightly" >> "$GITHUB_OUTPUT"
fi

- name: Check if wiki repository exists
id: wiki-check
run: |
Expand Down Expand Up @@ -76,22 +88,29 @@ jobs:
if git diff --cached --quiet; then
echo "No changes detected, skipping commit."
else
git commit -m "Update Subathon_Manager.drawio for release ${{ inputs.release_tag || 'nightly' }}"
git commit -m "Update Subathon_Manager.drawio for release ${{ steps.version.outputs.release_tag }}"
git push
fi

outputs:
release_tag: ${{ steps.version.outputs.release_tag }}

trigger-docs-release:
runs-on: ubuntu-latest
needs: update
if: ${{ (inputs.release_tag || 'nightly') != 'nightly' }}
if: ${{ needs.update.result == 'success' && needs.version.outputs.release_tag != 'nightly' && needs.version.outputs.release_tag != '' }}
steps:
- name: Dispatch docs release
uses: actions/github-script@v9
env:
VERSION: ${{ needs.version.outputs.release_tag }}
with:
github-token: ${{ secrets.DOCS_DISPATCH_TOKEN }}
script: |
const version = context.payload.inputs?.release_tag || 'nightly';
const version = process.env.VERSION;
if (!version || version === 'nightly') {
core.setFailed(`Refusing to dispatch a docs release for version '${version}'.`);
return;
}
await github.rest.repos.createDispatchEvent({
owner: 'WolfwithSword',
repo: 'SubathonManager-docs',
Expand Down
11 changes: 10 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,18 @@ riderModule.iml

exports/
imports/
cache/

*.smo
*.smw
*.smwc

presets/**
!presets/
!presets/**/
!presets/**/*.smw
!presets/**/*.smwc

# If we set this up
!.idea/codeStyles/
!.idea/inspectionProfiles/
!.idea/runConfigurations/
Expand Down
25 changes: 16 additions & 9 deletions SubathonManager.Core/AppServices.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using Updatum;
using System.Reflection;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
using Microsoft.Extensions.Logging;
namespace SubathonManager.Core {

Expand All @@ -26,9 +26,22 @@ public static string AppVersion
{
AssetExtensionFilter = "zip",
CurrentVersion = GetVersion(),
FetchOnlyLatestRelease = true
FetchOnlyLatestRelease = true,
AssetRegexPattern = GetAssetRegexPattern(),
};

private static string GetAssetRegexPattern()
{
string rid =
OperatingSystem.IsWindows() ? "win-x64" :
OperatingSystem.IsLinux() ? "linux-x64" :
OperatingSystem.IsMacOS()
? (RuntimeInformation.OSArchitecture == Architecture.Arm64 ? "osx-arm64" : "osx-x64")
: string.Empty;

return string.IsNullOrEmpty(rid) ? string.Empty : $@"(?i).*{rid}.*\.zip$";
}

private static Version GetVersion()
{
if (string.IsNullOrWhiteSpace(AppVersion))
Expand Down Expand Up @@ -96,13 +109,7 @@ public static async Task<bool> InstallUpdate(UpdatumDownloadedAsset? asset, ILog

try
{
Process.Start(new ProcessStartInfo
{
FileName = Config.AppFolder,
UseShellExecute = true,
Verb = "open"
});
await AppUpdater.InstallUpdateAsync(asset, true, UpdatumManager.NoRunAfterUpgradeToken);
await AppUpdater.InstallUpdateAsync(asset, true);
return true;
}
catch (Exception ex)
Expand Down
4 changes: 4 additions & 0 deletions SubathonManager.Core/Enums/SubathonPromptType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,11 @@ public static SubathonPromptSubType[] GetValidSubTypes(
return type.GetValidSubTypes();

if (filterEventType.IsSubscription())
{
if (filterEventType == SubathonEventType.YouTubeGiftMembership) // no tier selection
return [SubathonPromptSubType.Default];
return [SubathonPromptSubType.Default, SubathonPromptSubType.ByTier];
}
if (filterEventType.IsOrder())
{
// makeship and juniper only ever track unit counts between polls, but do it diff
Expand Down
23 changes: 23 additions & 0 deletions SubathonManager.Core/Enums/WidgetType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,29 @@ public enum WidgetType
Video = 2
}

public enum WidgetContextAction
{
ToggleVisibility,
Clone,
Delete,
ResetScale,
Refresh
}

public enum WidgetDisplayKind
{
Widget,//
UnpackedWidget,//
Image,
Video
}

public enum WidgetCatalogSource
{
Imported,
Preset
}

public static class WidgetTypeHelper
{
private static readonly HashSet<string> ImageExtensions = new(StringComparer.OrdinalIgnoreCase)
Expand Down
8 changes: 7 additions & 1 deletion SubathonManager.Core/Events/SubathonEvents.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ public static class SubathonEvents
public static event Action<List<SubathonValueDto>>? SubathonValuesPatched;

public static event Action<SubathonTotals>? SubathonTotalsUpdated;

public static event Action<SubscriptionTotals>? SubscriptionTotalsUpdated;

public static event Action<SubathonPromptRun, SubathonPrompt?>? PromptRunStarted;
public static event Action<SubathonPromptRun, SubathonPrompt?>? PromptRunUpdate;
public static event Action<SubathonPromptRun, long>? PromptRunProgressUpdated;
Expand All @@ -34,6 +35,11 @@ public static void RaiseSubathonTotalsUpdated(SubathonTotals totals)
SubathonTotalsUpdated?.Invoke(totals);
}

public static void RaiseSubscriptionTotalsUpdated(SubscriptionTotals totals)
{
SubscriptionTotalsUpdated?.Invoke(totals);
}


public static void RaisePromptRunStarted(SubathonPromptRun run, SubathonPrompt? prompt)
=> PromptRunStarted?.Invoke(run, prompt);
Expand Down
9 changes: 8 additions & 1 deletion SubathonManager.Core/Events/WidgetEvents.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using SubathonManager.Core.Models;
using SubathonManager.Core.Enums;
using SubathonManager.Core.Models;
using System.Diagnostics.CodeAnalysis;
namespace SubathonManager.Core.Events;

Expand All @@ -9,6 +10,12 @@ public static class WidgetEvents
public static event Action<Widget>? WidgetScaleUpdated;
public static event Action<Widget>? WidgetSizeUpdated;
public static event Action<Guid>? SelectEditorWidget;
public static event Action<Guid, WidgetContextAction>? WidgetActionRequested;

public static void RaiseWidgetAction(Guid widgetId, WidgetContextAction action)
{
WidgetActionRequested?.Invoke(widgetId, action);
}

public static void RaisePositionUpdated(Widget widget)
{
Expand Down
30 changes: 30 additions & 0 deletions SubathonManager.Core/Interfaces/IWidgetFileSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
namespace SubathonManager.Core.Interfaces;

public interface IWidgetFileSystem
{
bool Exists(string path);
string? ReadAllText(string path);
byte[]? ReadAllBytes(string path);
bool IsPacked(string path);
string? GetRealFilePath(string path);
IEnumerable<string> EnumerateFiles(string directory);
}

public sealed class DiskWidgetFileSystem : IWidgetFileSystem
{
public bool Exists(string path) => File.Exists(path);
public string? ReadAllText(string path) => File.Exists(path) ? File.ReadAllText(path) : null;
public byte[]? ReadAllBytes(string path) => File.Exists(path) ? File.ReadAllBytes(path) : null;
public bool IsPacked(string path) => false;
public string? GetRealFilePath(string path) => File.Exists(path) ? path : null;

public IEnumerable<string> EnumerateFiles(string directory) =>
Directory.Exists(directory)
? Directory.EnumerateFiles(directory, "*", SearchOption.AllDirectories)
: [];
}

public static class WidgetFiles
{
public static IWidgetFileSystem Current { get; set; } = new DiskWidgetFileSystem();
}
5 changes: 5 additions & 0 deletions SubathonManager.Core/Models/StateValue.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,9 @@ public class StateValue
public static class StateKeys
{
public const string WheelSpinsOwed = "WheelSpinsOwed";

public const string WidgetBrowserCollapsed = "WidgetBrowserCollapsed";
public const string WidgetBrowserSkipDeleteConfirm = "WidgetBrowserSkipDeleteConfirm";
public const string WidgetBrowserAllVersions = "WidgetBrowserAllVersions";
public const string EditorPreviewLightBg = "EditorPreviewLightBg";
}
46 changes: 33 additions & 13 deletions SubathonManager.Core/Models/Widget.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.ComponentModel;
using SubathonManager.Core.Enums;
using SubathonManager.Core.Interfaces;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Diagnostics.CodeAnalysis;
Expand Down Expand Up @@ -155,7 +156,11 @@ WidgetVariableType.StringList or
}
else if (((WidgetVariableType?)Type).IsFileVariable())
{
if (!Value.StartsWith("./") && !string.IsNullOrWhiteSpace(Value))
if (ResourcePaths.IsResourceUrl(Value))
{
sb.Append($"\"{Value}\"");
}
else if (!Value.StartsWith("./") && !string.IsNullOrWhiteSpace(Value))
{
sb.Append($"\"externalPath/{Value}\"");
}
Expand Down Expand Up @@ -300,36 +305,51 @@ public void ScanCssVariables()
CssVariables = ExtractCssVariablesFromFiles();
}

public List<CssVariable> ExtractCssVariablesFromFiles()
public List<string> GetLinkedCssPaths()
{
var extractedVars = new List<CssVariable>();
var paths = new List<string>();

if (!File.Exists(HtmlPath))
return extractedVars;
var html = WidgetFiles.Current.ReadAllText(HtmlPath);
if (html == null)
return paths;

string htmlContent = File.ReadAllText(HtmlPath);
string baseDir = Path.GetDirectoryName(HtmlPath)!;

var cssMatches = CssLinkRegex().Matches(htmlContent);

foreach (Match match in cssMatches)
foreach (Match match in CssLinkMatches(html))
{
string cssFile = match.Groups[1].Value;
string cssPath = Path.IsPathRooted(cssFile)
? cssFile
: Path.Combine(baseDir, cssFile);

if (!File.Exists(cssPath))
if (!WidgetFiles.Current.Exists(cssPath))
continue;
if (paths.Contains(cssPath, StringComparer.OrdinalIgnoreCase))
continue;

string cssContent = File.ReadAllText(cssPath);
paths.Add(cssPath);
}

return paths;
}

public static MatchCollection CssLinkMatches(string htmlContent) => CssLinkRegex().Matches(htmlContent);

public List<CssVariable> ExtractCssVariablesFromFiles()
{
var extractedVars = new List<CssVariable>();

foreach (var cssPath in GetLinkedCssPaths())
{
string? cssContent = WidgetFiles.Current.ReadAllText(cssPath);
if (cssContent == null) continue;
var varMatches = CssVarRegex().Matches(cssContent);

string metaPath = cssPath + ".json";
Dictionary<string, Dictionary<string, string>>? varTypes = null;
if (File.Exists(metaPath))
var metaJson = WidgetFiles.Current.ReadAllText(metaPath);
if (metaJson != null)
{
var metaJson = File.ReadAllText(metaPath);
varTypes = JsonSerializer.Deserialize<Dictionary<string, Dictionary<string, string>>>(metaJson,
new JsonSerializerOptions { PropertyNameCaseInsensitive = false });
}
Expand Down
34 changes: 34 additions & 0 deletions SubathonManager.Core/Models/WidgetCatalogEntry.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using System.ComponentModel.DataAnnotations;
using System.Diagnostics.CodeAnalysis;
using SubathonManager.Core.Enums;

namespace SubathonManager.Core.Models;

[ExcludeFromCodeCoverage]
public class WidgetCatalogEntry
{
[Key]
public int Id { get; set; }

public string PackPath { get; set; } = string.Empty;
public long FileSize { get; set; }
public long FileModifiedTicks { get; set; }

public WidgetCatalogSource Source { get; set; } = WidgetCatalogSource.Imported;

public string PackId { get; set; } = string.Empty;
public string Name { get; set; } = string.Empty;
public string Author { get; set; } = string.Empty;
public string Group { get; set; } = string.Empty;
public string Version { get; set; } = string.Empty;
public string Entry { get; set; } = string.Empty;
public string DocsUrl { get; set; } = string.Empty;
public string Tags { get; set; } = string.Empty;
public string PreviewImage { get; set; } = string.Empty;
public string PreviewCachePath { get; set; } = string.Empty;

public DateTime LastSeenUtc { get; set; }

public float ScaleX { get; set; } = 1;
public float ScaleY { get; set; } = 1;
}
20 changes: 20 additions & 0 deletions SubathonManager.Core/Objects/SubscriptionTotals.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using SubathonManager.Core.Enums;

namespace SubathonManager.Core.Objects;

public class SubscriptionTotals
{
public int SubTotal { get; init; } = 0;
public Dictionary<SubathonEventType, int> SubTotalByEvent { get; init; } = new();

public Dictionary<SubathonEventType, Dictionary<string, int>> SubTotalByEventTier { get; init; } = new();

public SubscriptionSimulatedTotals Simulated { get; init; } = new();
}

public class SubscriptionSimulatedTotals
{
public int SubTotal { get; init; } = 0;
public Dictionary<SubathonEventType, int> SubTotalByEvent { get; init; } = new();
public Dictionary<SubathonEventType, Dictionary<string, int>> SubTotalByEventTier { get; init; } = new();
}
Loading
Loading