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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 0 additions & 8 deletions src/SharpClient.App/MauiProgram.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ public static MauiApp CreateMauiApp()

builder.Services.AddMauiBlazorWebView();

// ── Crash / diagnostics file logging ──────────────────────────────
// A single FileLogStore is the sink for both ILogger output (via FileLoggerProvider — this
// captures Blazor's own Error-level log for an unhandled component exception, the "An
// unhandled error has occurred" case) and the global unhandled-exception hooks below. The
Expand Down Expand Up @@ -69,7 +68,6 @@ public static MauiApp CreateMauiApp()
builder.Logging.AddDebug();
#endif

// ── TNC telnet runtime ─────────────────────────────────────────────
// AddTelnetClient() is an extension method from TelnetNegotiationCore and
// registers ITelnetInterpreterFactory. The TNC package IS referenced in
// App.csproj so AddTelnetClient() resolves, but direct use of
Expand All @@ -79,7 +77,6 @@ public static MauiApp CreateMauiApp()
builder.Services.AddTelnetClient();
builder.Services.AddSingleton<ITelnetConnectionFactory, TelnetConnectionFactory>();

// ── Platform services ─────────────────────────────────────────────
#if ANDROID
builder.Services.AddSingleton<IConnectionKeepAlive, AndroidConnectionKeepAlive>();
#else
Expand All @@ -92,28 +89,23 @@ public static MauiApp CreateMauiApp()
builder.Services.AddSingleton<INotifier, MauiNotifier>();
builder.Services.AddSingleton<SharpClient.Core.Platform.IPreferences, MauiPreferences>();

// ── Data / persistence ────────────────────────────────────────────
// AppDbContext is transient so each call gets a fresh context; this
// avoids cross-thread SQLite issues and mirrors the Web's per-request
// scoped pattern without requiring HTTP request scopes in MAUI.
builder.Services.AddTransient<AppDbContext>();
builder.Services.AddTransient<IWorldStore, WorldStore>();
builder.Services.AddTransient<ISessionHistory, SessionHistory>();

// ── Session management ────────────────────────────────────────────
builder.Services.AddSingleton<SessionManager>();
builder.Services.AddSingleton<ISessionManager>(sp =>
sp.GetRequiredService<SessionManager>());

// ── Session launcher (real telnet) ────────────────────────────────
builder.Services.AddTransient<ISessionLauncher, SharpClient.Core.Sessions.TelnetSessionLauncher>();

// ── View models ───────────────────────────────────────────────────
// Registered via the shared extension so MAUI and Web stay in lockstep (no host drift).
// Per-view view models are Transient here to match MAUI's per-request-less lifetime model.
builder.Services.AddSharpClientViewModels(ServiceLifetime.Transient);

// ── Trigger / alias engines (stateless) ──────────────────────────
builder.Services.AddSingleton<ITriggerEngine, TriggerEngine>();
builder.Services.AddSingleton<IAliasEngine, AliasEngine>();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@

namespace SharpClient.App;

// ── Foreground-service declaration ────────────────────────────────────────────
//
// foregroundServiceType = "connectedDevice"
// Android docs: "remote device connected through Bluetooth, NFC, IR, USB, or network."
// A MUSH telnet server is a remote device connected over the network — connectedDevice is the
Expand All @@ -20,28 +18,21 @@ namespace SharpClient.App;
// foregroundServiceType attribute (which cannot be set via the C# ServiceAttribute). It MUST stay
// in lock-step with <ApplicationId> in SharpClient.App.csproj and the <service> entry in the
// manifest, otherwise the merger emits two distinct services and startForeground throws.
//
[Service(Exported = false, Name = "com.sharpmush.sharpclient.app.ConnectionKeepAliveService")]
[SupportedOSPlatform("android26.0")] // Foreground services + NotificationChannel require API 26
internal sealed class ConnectionKeepAliveService : Service
{
// ── Notification constants ────────────────────────────────────────────────

private const string ChannelId = "sharpclient_keepalive";
private const string ChannelName = "Connection Keep-Alive";
private const int NotificationId = 1_001;
private const string WakeLockTag = "SharpClient::KeepAlive";

// ── Runtime state ─────────────────────────────────────────────────────────

private PowerManager.WakeLock? _wakeLock;
private ConnectivityManager? _connectivity;
private ConnectivityManager.NetworkCallback? _networkCallback;
private Network? _boundNetwork;
private bool _haveSeenNetwork;

// ── Service lifecycle ─────────────────────────────────────────────────────

public override IBinder? OnBind(Intent? intent) => null;

[return: GeneratedEnum]
Expand Down Expand Up @@ -72,8 +63,6 @@ public override void OnDestroy()
base.OnDestroy();
}

// ── Wake lock ─────────────────────────────────────────────────────────────
//
// A PARTIAL_WAKE_LOCK keeps the CPU running so the TCP socket and telnet-NOP keepalive keep
// ticking while the screen is off / the device dozes. Released on OnDestroy (i.e. when the
// coordinator Halts the service because no sessions remain connected).
Expand Down Expand Up @@ -101,8 +90,6 @@ private void ReleaseWakeLock()
_wakeLock = null;
}

// ── Connectivity monitoring ───────────────────────────────────────────────
//
// Watches the device's DEFAULT network. When it changes on the move (WiFi↔cellular, tower
// handoff, coming out of a dead zone) the old sockets are silently dead. We (a) pin the process
// to the new network so reconnects use the live interface at once, and (b) raise a signal that
Expand Down Expand Up @@ -183,8 +170,6 @@ private void OnNetworkLost(Network network)
}
}

// ── Helpers ───────────────────────────────────────────────────────────────

private void EnsureNotificationChannel()
{
var channel = new NotificationChannel(ChannelId, ChannelName, NotificationImportance.Low)
Expand Down
1 change: 0 additions & 1 deletion src/SharpClient.App/Platforms/MacCatalyst/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ namespace SharpClient.App;

public class Program
{
// This is the main entry point of the application.
static void Main(string[] args)
{
// if you want to use a different Application Delegate class from "AppDelegate"
Expand Down
1 change: 0 additions & 1 deletion src/SharpClient.App/Platforms/iOS/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ namespace SharpClient.App;

public class Program
{
// This is the main entry point of the application.
static void Main(string[] args)
{
// if you want to use a different Application Delegate class from "AppDelegate"
Expand Down
5 changes: 0 additions & 5 deletions src/SharpClient.Core/Presentation/SettingsViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ namespace SharpClient.Core.Presentation;

public sealed class SettingsViewModel
{
// ── Lookup tables ────────────────────────────────────────────────────────
public static readonly string[] FontOptions =
[
"JetBrains Mono",
Expand Down Expand Up @@ -42,7 +41,6 @@ public sealed class SettingsViewModel
["Courier"] = "Courier,'Courier New',monospace",
};

// ── State ────────────────────────────────────────────────────────────────
private readonly IPreferences _prefs;
private string _outputFont;
private int _minColumns;
Expand All @@ -53,7 +51,6 @@ public sealed class SettingsViewModel

public event Action? Changed;

// ── Constructor ──────────────────────────────────────────────────────────
public SettingsViewModel(IPreferences prefs)
{
_prefs = prefs;
Expand All @@ -72,7 +69,6 @@ public SettingsViewModel(IPreferences prefs)
}
}

// ── Properties ───────────────────────────────────────────────────────────
public string OutputFont
{
get => _outputFont;
Expand Down Expand Up @@ -109,7 +105,6 @@ public bool Scanlines
set { _scanlines = value; _prefs.SetBool("Scanlines", value); Changed?.Invoke(); }
}

// ── Computed ─────────────────────────────────────────────────────────────
public string FontFamily =>
s_fontFamilies.TryGetValue(_outputFont, out var f) ? f
: "'JetBrains Mono',ui-monospace,monospace";
Expand Down
10 changes: 0 additions & 10 deletions src/SharpClient.Core/Presentation/TriggerAliasEditorViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ public sealed class TriggerAliasEditorViewModel

public TriggerAliasEditorViewModel(IWorldStore store) => _store = store;

// ── Scope ─────────────────────────────────────────────────────────────

/// <summary>null means world scope; non-null is the character's Id.</summary>
public Guid? CharacterScopeId => _characterScopeId;

Expand Down Expand Up @@ -42,15 +40,11 @@ public void SetScope(Guid? characterId)
RaiseChanged();
}

// ── Exposed lists ─────────────────────────────────────────────────────

public IReadOnlyList<TriggerRule> Triggers => ScopedTriggers;
public IReadOnlyList<AliasRule> Aliases => ScopedAliases;

public event Action? Changed;

// ── Load / Set ────────────────────────────────────────────────────────

public async Task LoadAsync(Guid worldId, CancellationToken ct = default)
{
_worldId = worldId;
Expand All @@ -72,8 +66,6 @@ public void SetWorld(World world)
RaiseChanged();
}

// ── Triggers ──────────────────────────────────────────────────────────

public async Task AddTriggerAsync(TriggerRule rule, CancellationToken ct = default)
{
if (_world is null) return;
Expand Down Expand Up @@ -110,8 +102,6 @@ public async Task ToggleTriggerAsync(Guid id, CancellationToken ct = default)
await LoadAsync(_worldId, ct);
}

// ── Aliases ───────────────────────────────────────────────────────────

public async Task AddAliasAsync(AliasRule rule, CancellationToken ct = default)
{
if (_world is null) return;
Expand Down
4 changes: 0 additions & 4 deletions src/SharpClient.Data/SessionHistory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@ public SessionHistory(IAppStorage storage)
_dbPath = storage.GetDatabasePath();
}

// ── ISessionHistory ──────────────────────────────────────────────────────

public async Task AppendAsync(Guid characterId, string line, CancellationToken cancellationToken = default)
{
await using var connection = new SqliteConnection($"Data Source={_dbPath}");
Expand Down Expand Up @@ -64,8 +62,6 @@ public async Task<IReadOnlyList<HistoryHit>> SearchAsync(
return results;
}

// ── Helpers ──────────────────────────────────────────────────────────────

/// <summary>
/// Creates the FTS5 table if it does not yet exist.
/// <c>CREATE VIRTUAL TABLE IF NOT EXISTS</c> is idempotent so this is safe to
Expand Down
4 changes: 0 additions & 4 deletions src/SharpClient.Data/WorldStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@ public sealed class WorldStore : IWorldStore

public WorldStore(AppDbContext db) => _db = db;

// ── IWorldStore ──────────────────────────────────────────────────────────

public async Task<IReadOnlyList<World>> GetWorldsAsync(CancellationToken cancellationToken = default)
{
await EnsureSchemaAsync(cancellationToken);
Expand Down Expand Up @@ -103,8 +101,6 @@ public async Task DeleteWorldAsync(Guid worldId, CancellationToken cancellationT
}
}

// ── Helpers ──────────────────────────────────────────────────────────────

private async Task EnsureSchemaAsync(CancellationToken cancellationToken)
{
if (_schemaEnsured) return;
Expand Down
2 changes: 0 additions & 2 deletions src/SharpClient.UI/ServiceCollectionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,13 @@ public static IServiceCollection AddSharpClientViewModels(
this IServiceCollection services,
ServiceLifetime perViewLifetime)
{
// ── Shared-lifetime view models (Singleton in both hosts) ────────────
services.AddSingleton<SessionsViewModel>(sp =>
new SessionsViewModel(sp.GetRequiredService<ISessionManager>()));
services.AddSingleton<ProtocolPanelViewModel>(sp =>
new ProtocolPanelViewModel(sp.GetRequiredService<ISessionManager>()));
services.AddSingleton<SettingsViewModel>(sp =>
new SettingsViewModel(sp.GetRequiredService<IPreferences>()));

// ── Per-view view models (Transient in MAUI, Scoped in Web) ──────────
services.Add(new ServiceDescriptor(
typeof(WorldManagerViewModel),
sp => new WorldManagerViewModel(
Expand Down
9 changes: 0 additions & 9 deletions src/SharpClient.Web/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,41 +15,32 @@
builder.Services.AddRazorComponents()
.AddInteractiveServerComponents();

// ── TNC telnet runtime ─────────────────────────────────────────────────────
builder.Services.AddTelnetClient();
builder.Services.AddSingleton<ITelnetConnectionFactory, TelnetConnectionFactory>();

// ── Platform services ──────────────────────────────────────────────────────
builder.Services.AddSingleton<IPreferences, WebPreferences>();

builder.Services.AddSingleton<IAppStorage, WebAppStorage>();
builder.Services.AddSingleton<ISecretStore, WebSecretStore>();

// ── Trigger / alias engines (stateless singletons) ─────────────────────────
builder.Services.AddSingleton<ITriggerEngine, TriggerEngine>();
builder.Services.AddSingleton<IAliasEngine, AliasEngine>();

// ── Platform notifier ──────────────────────────────────────────────────────
builder.Services.AddSingleton<INotifier, WebNotifier>();

// ── Diagnostics ─────────────────────────────────────────────────────────────
// The Web preview has no persistent file log; the no-op exporter keeps SettingsView's
// ILogExporter injection satisfiable and hides the export affordance (IsAvailable == false).
builder.Services.AddSingleton<ILogExporter, NoopLogExporter>();

// ── Session management ─────────────────────────────────────────────────────
builder.Services.AddSingleton<SessionManager>();
builder.Services.AddSingleton<ISessionManager>(sp => sp.GetRequiredService<SessionManager>());

// ── Data / persistence ─────────────────────────────────────────────────────
builder.Services.AddScoped<AppDbContext>();
builder.Services.AddScoped<IWorldStore, WorldStore>();
builder.Services.AddScoped<ISessionHistory, SessionHistory>();

// ── Session launcher (real telnet) ─────────────────────────────────────────
builder.Services.AddScoped<ISessionLauncher, TelnetSessionLauncher>();

// ── View models ────────────────────────────────────────────────────────────
// Registered via the shared extension so MAUI and Web stay in lockstep (no host drift).
// Per-view view models are Scoped here to match the Web's per-request scope.
builder.Services.AddSharpClientViewModels(ServiceLifetime.Scoped);
Expand Down
Loading
Loading