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
2 changes: 2 additions & 0 deletions CHANGELOG.en-US.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
English (US) release notes, mirroring CHANGELOG.md (Brazilian Portuguese). Before publishing a version, add a `## [VERSION]` section to **both** files: the release workflow uses the section matching the tag and fails if either is missing.

## [Unreleased]
### What's new
- **Checks and updates by itself on open**: ControlFS now looks for a new version **every time it opens** (before, at most once a day) and, in the installed build, if a verified version exists (signed manifest, SHA-256 and size) and you haven't started using the app and nothing is running, it **downloads, installs and reopens already updated**, without asking. If you already started using it, the usual notice stays (Install and restart / Later). Turn it off in Menu → Configurações → Atualizações → **Atualizar sozinho ao abrir**. The portable build only notifies. It never runs in CI or with `--no-auto-update`.

## [0.13.0-alpha.1]
### What's new
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
Notas em português do Brasil; a versão em inglês (Estados Unidos) fica em `CHANGELOG.en-US.md`. Antes de publicar uma versão, adicione uma seção `## [VERSÃO]` **nos dois arquivos**. O workflow de release usa a seção da tag e falha se faltar alguma.

## [Unreleased]
### Novidades
- **Verifica e se atualiza sozinho ao abrir**: o ControlFS agora procura versão nova **a cada vez que abre** (antes, no máximo uma vez por dia) e, na versão instalada, se há uma versão verificada (manifesto assinado, SHA-256 e tamanho) e você ainda não começou a usar o app e nada está em andamento, **baixa, instala e reabre já atualizado**, sem perguntar. Se você já começou a usar, fica o aviso de sempre (Instalar e reiniciar / Depois). Desligue em Menu → Configurações → Atualizações → **Atualizar sozinho ao abrir**. A versão portátil só avisa. Nunca roda em CI nem com `--no-auto-update`.

## [0.13.0-alpha.1]
### Novidades
Expand Down
6 changes: 3 additions & 3 deletions docs/GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -290,11 +290,11 @@ North → **Abrir terminal aqui…** (open terminal here) opens **Windows Termin

The **installed** version updates itself:

1. Once a day at most, it asks GitHub for the latest release of `nextestudios/ControlFS` (stable only, or also pre-releases if you are on one).
1. Each time the app opens, it asks GitHub for the latest release of `nextestudios/ControlFS` (stable only, or also pre-releases if you are on one).
2. It downloads the installer in the background and only accepts it if the **release manifest is signed with the project key** and the file's **SHA-256 and size** match. Same or older versions are refused.
3. It offers **Install and restart**. Postpone it and it installs silently when you quit. Nothing happens while a copy or extraction is running.
3. Right after opening, if you haven't started using the app yet and nothing is running, it **installs and reopens by itself on the new version** (**Atualizar sozinho ao abrir**, on by default). If you already started using it or turned the option off, it offers **Install and restart**; postpone it and it installs silently when you quit. Nothing happens while a copy or extraction is running. It never runs in CI or with `--no-auto-update` (or the `CONTROLFS_NO_AUTO_UPDATE` variable set).

Menu → Configurações → **Updates**: check now, automatic check on/off, install on quit on/off, pre-releases (automatic / yes / no).
Menu → Configurações → **Updates**: check now, automatic check on/off, update by itself on open on/off, install on quit on/off, pre-releases (automatic / yes / no).
The **portable** version only tells you a new version exists; download it from the release page.

## Automation
Expand Down
6 changes: 3 additions & 3 deletions docs/GUIDE.pt-BR.md
Original file line number Diff line number Diff line change
Expand Up @@ -290,11 +290,11 @@ Norte → **Abrir terminal aqui…** abre o **Windows Terminal** na pasta atual,

A versão **instalada** se atualiza sozinha:

1. No máximo uma vez por dia, pergunta ao GitHub qual é a última release de `nextestudios/ControlFS` (só estáveis, ou também pré-lançamentos se você estiver num).
1. A cada vez que o app abre, pergunta ao GitHub qual é a última release de `nextestudios/ControlFS` (só estáveis, ou também pré-lançamentos se você estiver num).
2. Baixa o instalador em segundo plano e só o aceita se o **manifesto da release estiver assinado com a chave do projeto** e o **SHA-256 e o tamanho** do arquivo conferirem. Versões iguais ou anteriores são recusadas.
3. Oferece **Instalar e reiniciar**. Se você adiar, instala em silêncio ao sair. Nada acontece enquanto uma cópia ou extração estiver em andamento.
3. Logo depois de abrir, se você ainda não começou a usar o app e nada está em andamento, **instala e reabre sozinho já na versão nova** (**Atualizar sozinho ao abrir**, ligado por padrão). Se você já começou a usar ou desligou a opção, oferece **Instalar e reiniciar**; se adiar, instala em silêncio ao sair. Nada acontece enquanto uma cópia ou extração estiver em andamento.

Menu → Configurações → **Atualizações**: verificar agora, verificação automática sim/não, instalar ao sair sim/não, pré-lançamentos (automático / sim / não).
Menu → Configurações → **Atualizações**: verificar agora, verificação automática sim/não, atualizar sozinho ao abrir sim/não, instalar ao sair sim/não, pré-lançamentos (automático / sim / não).
A versão **portátil** só avisa que existe versão nova; baixe-a na página da release.

## Automação
Expand Down
6 changes: 3 additions & 3 deletions docs/PRIVACY.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ the user or the person installing or operating it.
- **No telemetry, no analytics, no accounts.** Files, folder names, search terms, passwords and settings never leave the PC.
- **Local data only:** settings (including the names and searches you typed, kept for keyboard suggestions until you turn them off, and the folders of your open tabs, kept to restore them until you turn that off; never passwords), history, where each video stopped (only a SHA-256 digest of the file's path, size and date plus the seconds — never the name; Configurações → Apagar onde os vídeos pararam erases them), controller profiles and logs live in `%LOCALAPPDATA%\ControlFS` (installed) or
in `ControlFS_Data` next to `ControlFS-Portable-x64.exe` (portable). Passwords are never saved or logged.
- **Update check (optional):** the app asks GitHub's servers (the GitHub API and release downloads) whether a newer release exists, at most
once a day, and downloads it only from the project's GitHub releases. The request carries only a `ControlFS/<version>`
- **Update check (optional):** the app asks GitHub's servers (the GitHub API and release downloads) whether a newer release exists, each time
the app opens, and downloads it only from the project's GitHub releases. The request carries only a `ControlFS/<version>`
User-Agent; GitHub sees the IP address like for any web request ([GitHub privacy statement](https://docs.github.com/site-policy/privacy-policies/github-general-privacy-statement)).
Turn it off in Menu → Atualizações → "Verificar automaticamente"; a manual "Verificar agora" still works on request.
- **Network locations:** ControlFS lists the mapped drives and network locations Windows already knows (read locally) and
Expand All @@ -34,7 +34,7 @@ especificamente pelo usuário ou por quem o instala ou opera.
- **Só dados locais:** preferências (inclusive nomes e buscas digitados, guardados para as sugestões do teclado até você desligá-las, e as pastas das abas abertas, guardadas para restaurá-las até você desligar; nunca senhas), histórico, onde cada vídeo parou (só um resumo SHA-256 do caminho, tamanho e data do arquivo e os segundos — nunca o nome; Configurações → Apagar onde os vídeos pararam apaga), perfis de controle e logs ficam em `%LOCALAPPDATA%\ControlFS` (instalado)
ou em `ControlFS_Data` ao lado do `ControlFS-Portable-x64.exe` (portátil). Senhas nunca são salvas nem registradas.
- **Verificação de atualizações (opcional):** o app pergunta aos servidores do GitHub (API e downloads das releases) se há versão nova,
no máximo uma vez por dia, e só baixa das releases do projeto no GitHub. O pedido leva apenas o User-Agent
a cada abertura do app, e só baixa das releases do projeto no GitHub. O pedido leva apenas o User-Agent
`ControlFS/<versão>`; o GitHub vê o endereço IP como em qualquer acesso à web. Desligue em Menu → Atualizações → "Verificar automaticamente"; "Verificar agora" continua funcionando quando pedido.
- **Locais de rede:** o ControlFS lista as unidades mapeadas e os locais de rede que o Windows já conhece (lidos
localmente) e só contata um servidor quando você abre aquele local. Não guarda credenciais de rede.
Expand Down
5 changes: 5 additions & 0 deletions docs/TESTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -861,3 +861,8 @@ Só a CI (WinRAR 7.23 pelo winget, arquivo criado pelo Rar.exe real e extraído
- [ ] Menu → Configurações → Sons do controle → baixo/médio/alto: mover o foco, confirmar, voltar e marcar tocam sons diferentes e discretos; o volume muda entre as opções; "desligados" silencia tudo.
- [ ] Segurar o direcional numa lista longa: o tique não vira chiado nem atrasa o foco. O teclado (setas, Enter, Esc) não toca nada.
- [ ] Com fones/alto-falantes diferentes ou sem saída de áudio: segue a saída padrão do Windows; sem dispositivo, o app funciona igual.

## Atualização automática ao abrir — não validado em hardware
- [ ] Instalado numa versão antiga, com internet: abrir o app sem tocar em nada. Ele baixa a versão nova, fecha e reabre sozinho já atualizada (Menu → Sobre mostra a nova).
- [ ] Abrir e mexer no controle antes de terminar o download: aparece "Atualização pronta" (Instalar e reiniciar / Depois), sem reiniciar sozinho.
- [ ] Menu → Configurações → Atualizações → "Atualizar sozinho ao abrir: não": só o aviso. Com uma cópia em andamento nunca reinicia.
4 changes: 4 additions & 0 deletions src/ControlFS.App/Views/MainWindow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,10 @@ internal MainWindow(string? dataDirectory)
PlaybackPositions = new JsonPlaybackPositionStore(data),
// Boas-vindas (#231) só na primeira execução do app de verdade: nunca nas capturas nem com --no-onboarding.
DeferUpdateCheckToFirstFrame = dataDirectory is null,
// Nunca em CI nem quando um script de teste abre o app: a atualização automática fecharia o app no meio do smoke.
AllowAutoInstall = dataDirectory is null && Environment.GetEnvironmentVariable("CI") is null
&& Environment.GetEnvironmentVariable("CONTROLFS_NO_AUTO_UPDATE") is null
&& !Environment.GetCommandLineArgs().Contains("--no-auto-update", StringComparer.OrdinalIgnoreCase),
Trace = dataDirectory is null ? AppLog.Info : null,
OfferOnboarding = dataDirectory is null && !Environment.GetCommandLineArgs().Contains("--no-onboarding", StringComparer.OrdinalIgnoreCase),
// "Mais da equipe": uma vez, depois das boas-vindas; mesma regra (nunca nas capturas nem com --no-onboarding).
Expand Down
2 changes: 1 addition & 1 deletion src/ControlFS.Application/AppController.Onboarding.cs
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ private void LoadOnboardingStep(OnboardingModal modal, int index, bool keepFocus
{
UpdateSettings(s => s with { AutoCheckUpdates = !s.AutoCheckUpdates });
Reload();
}, ActionIcon.Update, Settings.AutoCheckUpdates ? "sim" : "não", "No máximo uma vez por dia, só nas versões publicadas do ControlFS no GitHub."),
}, ActionIcon.Update, Settings.AutoCheckUpdates ? "sim" : "não", "A cada abertura, só nas versões publicadas do ControlFS no GitHub; a versão instalada se atualiza sozinha ao abrir (dá para desligar em Atualizações)."),
next,
];
break;
Expand Down
32 changes: 29 additions & 3 deletions src/ControlFS.Application/AppController.Updates.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,14 @@ public enum UpdateState

public sealed partial class AppController
{
private static readonly TimeSpan AutomaticCheckInterval = TimeSpan.FromHours(24);
/// <summary>
/// Falso em CI e quando o app é aberto por um script de teste (o app de verdade liga na janela): a atualização
/// automática fecharia o app no meio do smoke. Os testes deixam ligado.
/// </summary>
public bool AllowAutoInstall { get; init; } = true;

/// <summary>Alguma ação do usuário já chegou desde a abertura: a atualização automática não reinicia mais o app por cima dele.</summary>
private bool _userActedSinceStart;
private readonly IUpdateService? _updates;
private bool _installerLaunched;

Expand Down Expand Up @@ -69,7 +76,6 @@ private void StartAutomaticUpdateCheck()
return;
}
if (_updates is null || !Settings.AutoCheckUpdates) return;
if (Settings.LastUpdateCheck is { } last && Now() - last < AutomaticCheckInterval) return;
Track(CheckForUpdatesAsync(manual: false));
}

Expand Down Expand Up @@ -117,6 +123,14 @@ private async Task DownloadUpdateAsync(UpdateManifest manifest, bool manual)
{
ReadyUpdate = await _updates!.DownloadAsync(manifest, null, CancellationToken.None);
UpdateState = UpdateState.Ready;
if (!manual && CanInstallAutomatically() && Settings.LastAutoInstallAttempt != manifest.Version.ToString())
{
UpdateSettings(s => s with { LastAutoInstallAttempt = manifest.Version.ToString() });
// Logo depois de abrir, sem nada em andamento e sem o usuário ter começado a usar: instala e reabre já na versão nova.
StatusMessage = $"Atualizando para {manifest.Version}…";
InstallUpdateNow();
if (_installerLaunched) return;
}
// Não interrompe o usuário no meio de um diálogo ou de uma operação de disco.
if (manual || (TopModal is null && Operations.ActiveCount == 0)) ShowUpdateReady();
else StatusMessage = $"Atualização {manifest.Version} pronta. Abra o menu para instalar.";
Expand All @@ -135,6 +149,14 @@ private async Task DownloadUpdateAsync(UpdateManifest manifest, bool manual)
}
}

/// <summary>
/// A atualização automática da abertura só vale para o app instalado, com a opção ligada, nas primeiras ações depois de abrir:
/// nenhuma operação, nenhuma janela aberta e nenhuma tecla ou botão ainda. Se o usuário já começou a usar, fica o aviso de sempre.
/// </summary>
private bool CanInstallAutomatically() =>
AllowAutoInstall && Settings.AutoInstallUpdates && _updates is { IsInstalled: true } && !_installerLaunched
&& !_userActedSinceStart && Operations.ActiveCount == 0 && TopModal is null;

private void ShowUpdateReady()
{
if (ReadyUpdate is not { } ready) return;
Expand Down Expand Up @@ -211,7 +233,11 @@ private void ShowUpdatesMenu()
Detail: $"Versão atual {_updates.CurrentVersion}" + (Settings.LastUpdateCheck is { } last ? $" · última verificação {last.LocalDateTime:g}" : string.Empty), Icon: ActionIcon.Refresh));
items.Add(new MenuItem($"Verificar automaticamente: {(Settings.AutoCheckUpdates ? "sim" : "não")}",
() => UpdateSettings(s => s with { AutoCheckUpdates = !s.AutoCheckUpdates }),
Detail: "Uma consulta por dia às releases do GitHub; nenhum dado pessoal é enviado.", Icon: ActionIcon.Operations));
Detail: "Uma consulta às releases do GitHub a cada vez que o app abre; nenhum dado pessoal é enviado.", Icon: ActionIcon.Operations));
items.Add(new MenuItem($"Atualizar sozinho ao abrir: {(Settings.AutoInstallUpdates ? "sim" : "não")}",
() => UpdateSettings(s => s with { AutoInstallUpdates = !s.AutoInstallUpdates }),
_updates.IsInstalled ? null : "Somente na versão instalada; a portátil é atualizada manualmente.",
Detail: "Ao abrir, se há versão nova (verificada), o ControlFS instala e reabre sozinho, desde que você ainda não tenha começado a usar e nada esteja em andamento.", Icon: ActionIcon.Update));
items.Add(new MenuItem($"Instalar ao sair: {(Settings.InstallUpdatesOnExit ? "sim" : "não")}",
() => UpdateSettings(s => s with { InstallUpdatesOnExit = !s.InstallUpdatesOnExit }),
_updates.IsInstalled ? null : "Somente na versão instalada; a portátil é atualizada manualmente.", Icon: ActionIcon.Exit));
Expand Down
1 change: 1 addition & 0 deletions src/ControlFS.Application/AppController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ public async Task WhenIdleAsync()

public void Handle(InputAction action)
{
_userActedSinceStart = true;
if (action.IsScroll())
{
// Rolagem contínua: chega muitas vezes por segundo; só redesenha quando algo mudou e mantém o aviso do rodapé.
Expand Down
14 changes: 13 additions & 1 deletion src/ControlFS.Core/Contracts/ISettingsStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -111,9 +111,21 @@ public sealed record AppSettings
/// <summary>Nomes e buscas concluídos no teclado virtual, o mais recente primeiro (lista limitada; nunca senhas).</summary>
public IReadOnlyList<string> TypedTexts { get; init; } = [];

/// <summary>Verifica novas versões ao abrir (no máximo uma vez por dia). Desligável no menu.</summary>
/// <summary>Verifica novas versões a cada abertura do app. Desligável no menu.</summary>
public bool AutoCheckUpdates { get; init; } = true;

/// <summary>
/// Atualização automática ao abrir (versão instalada): se a verificação da abertura achar uma versão nova e verificada, e
/// o usuário ainda não começou a usar o app, instala e reabre sozinho. Desligado, só oferece "Instalar e reiniciar".
/// </summary>
public bool AutoInstallUpdates { get; init; } = true;

/// <summary>
/// Última versão que a atualização automática tentou instalar. Se a mesma versão aparecer de novo na abertura seguinte
/// (a instalação não pegou), não tenta de novo sozinha: cai no aviso de sempre. Evita um laço de reinícios.
/// </summary>
public string? LastAutoInstallAttempt { get; init; }

/// <summary>Instala em silêncio, ao sair, uma atualização já baixada e verificada.</summary>
public bool InstallUpdatesOnExit { get; init; } = true;

Expand Down
Loading
Loading