Skip to content

Commit

Permalink
🍞 Show toast notification on startup (#32)
Browse files Browse the repository at this point in the history
The most recent FlashCom update failed Windows Store certification
because there was no clear indication that the application had launched
successfully.

This change adds a toast notification that triggers on startup to tell
the user that the app is running, and how to invoke it.

It can be disabled by setting `"showStartupNotification"` to `false` in
`settings.json`.
  • Loading branch information
haydenmc authored Oct 1, 2024
1 parent 8ff8235 commit 42ea779
Show file tree
Hide file tree
Showing 7 changed files with 81 additions and 11 deletions.
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,13 @@ For the packaged app, `settings.json` is located in `%LOCALAPPDATA%\Packages\164

For the unpackaged app, `settings.json` is located in `%LOCALAPPDATA%\FlashCom`.

The following fields can be set in the `settings.json` root object:

| Name | Type | Description |
| --------------------------- | ------------------------ | ------------------------------------------ |
| `"showStartupNotification"` | `true` / `false` | Determines whether the "FlashCom is running" toast notification is shown on startup. |
| `"commands"` | Array of Command Objects | The set of commands shown in FlashCom. |

## Commands

| Name | "type" | Description |
Expand Down
30 changes: 30 additions & 0 deletions src/FlashCom/App.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,26 @@ namespace
{
const std::unordered_set<uint32_t> c_hotkeyCombo{ VK_LWIN, VK_SPACE };
constexpr std::chrono::milliseconds c_hotkeyExpirationTime{ 1000 };

void ShowStartupNotification()
{
winrt::WDXD::XmlDocument notificationPayload;
notificationPayload.LoadXml(LR""""(
<toast launch="-from-startup-toast">
<visual>
<binding template="ToastGeneric">
<text>FlashCom is running!</text>
<text>Press Win+Space to invoke.</text>
<text>Manage settings from the system tray icon.</text>
</binding>
</visual>
</toast>
)"""");
winrt::WUIN::ToastNotification notification{ notificationPayload };
auto notificationManager{ winrt::WUIN::ToastNotificationManager::GetDefault() };
auto notifier{ notificationManager.CreateToastNotifier() };
notifier.Show(notification);
}
}

namespace FlashCom
Expand All @@ -24,6 +44,7 @@ namespace FlashCom
void App::LoadDataModel()
{
auto result{ m_settingsManager.LoadSettings() };
m_dataModel->ShowStartupNotification = m_settingsManager.GetShowStartupNotification();
m_dataModel->RootNode = m_settingsManager.GetCommandTreeRoot();
m_dataModel->CurrentNode = m_dataModel->RootNode.get();
if (!result.has_value())
Expand All @@ -38,6 +59,15 @@ namespace FlashCom

int App::RunMessageLoop()
{
// On launch, notify the user that we are, in fact, running.
// FlashCom has failed Windows Store certification in the past because
// the tester thought the app didn't start successfully.
if (m_dataModel->ShowStartupNotification)
{
SPDLOG_INFO("App::RunMessageLoop - Showing startup notification");
ShowStartupNotification();
}

MSG msg;
while (GetMessage(&msg, nullptr, 0, 0))
{
Expand Down
1 change: 1 addition & 0 deletions src/FlashCom/Models/DataModel.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ namespace FlashCom::Models
struct DataModel
{
std::string LoadErrorMessage;
bool ShowStartupNotification;
std::shared_ptr<TreeNode> RootNode;
TreeNode* CurrentNode{ nullptr };
};
Expand Down
29 changes: 28 additions & 1 deletion src/FlashCom/Settings/SettingsManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ namespace
constexpr std::string_view c_appDataLocalDirectoryName{ "FlashCom" };
constexpr std::string_view c_settingsFileName{ "settings.json" };
// JSON property names
constexpr std::string_view c_showStartupNotificationProperty{ "showStartupNotification" };
constexpr std::string_view c_commandsJsonProperty{ "commands" };
constexpr std::string_view c_commandNameJsonProperty{ "name" };
constexpr std::string_view c_commandKeyJsonProperty{ "key" };
Expand All @@ -29,6 +30,7 @@ namespace
constexpr std::string_view c_commandTypeValueAumid{ "aumid" };
// Default settings.json contents
constexpr std::string_view c_defaultSettingsFileContents{ R"({
"showStartupNotification": true,
"commands": [
{
"name": "README",
Expand Down Expand Up @@ -351,7 +353,7 @@ namespace FlashCom::Settings
return std::unexpected(std::format("Could not parse settings.json file: {}",
e.what()));
}

PopulateSettingsValues(settingsLock, settingsJson);
PopulateCommandTree(settingsLock, settingsJson);
return {}; // Spurious warning C4715
}
Expand All @@ -362,12 +364,37 @@ namespace FlashCom::Settings
return m_settingsFilePath;
}

bool SettingsManager::GetShowStartupNotification()
{
return m_showStartupNotification;
}

std::shared_ptr<Models::TreeNode> SettingsManager::GetCommandTreeRoot()
{
std::shared_lock settingsLock{ m_settingsAccessMutex };
return m_commandTreeRoot;
}

void SettingsManager::PopulateSettingsValues(
const std::unique_lock<std::shared_mutex>& /*accessLock*/,
const nlohmann::json& settingsJson)
{
SPDLOG_INFO("SettingsManager::PopulateSettingsValues");

if (settingsJson.contains(c_showStartupNotificationProperty) &&
settingsJson.at(c_showStartupNotificationProperty).is_boolean())
{
m_showStartupNotification =
settingsJson.at(c_showStartupNotificationProperty).get<bool>();
}
else
{
SPDLOG_INFO("SettingsManager::PopulateSettingsValues - No '{}' bool property found. "
"Defaulting to '{}'.", c_showStartupNotificationProperty,
m_showStartupNotification);
}
}

std::expected<void, std::string> SettingsManager::PopulateCommandTree(
const std::unique_lock<std::shared_mutex>& /*accessLock*/,
const nlohmann::json& settingsJson)
Expand Down
5 changes: 5 additions & 0 deletions src/FlashCom/Settings/SettingsManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,18 @@ namespace FlashCom::Settings
SettingsManager();
std::expected<void, std::string> LoadSettings();
std::filesystem::path GetSettingsFilePath();
bool GetShowStartupNotification();
std::shared_ptr<Models::TreeNode> GetCommandTreeRoot();

private:
std::filesystem::path const m_settingsFilePath;
std::shared_mutex m_settingsAccessMutex;
bool m_showStartupNotification{ true };
std::shared_ptr<Models::TreeNode> m_commandTreeRoot;

void PopulateSettingsValues(
const std::unique_lock<std::shared_mutex>& accessLock,
const nlohmann::json& settingsJson);
std::expected<void, std::string> PopulateCommandTree(
const std::unique_lock<std::shared_mutex>& accessLock,
const nlohmann::json& settingsJson);
Expand Down
16 changes: 6 additions & 10 deletions src/FlashCom/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,22 +40,18 @@ namespace
}
}

// Console entrypoint
int main(
int /*argc*/,
wchar_t* /*argv*/[]
)
{
return Run(GetModuleHandleW(nullptr));
}

// Windows entrypoint
int WINAPI wWinMain(
HINSTANCE hInstance,
HINSTANCE /*hPrevInstance*/,
LPWSTR /*lpCmdLine*/,
LPWSTR lpCmdLine,
int /*nCmdShow*/
)
{
// Ignore launches that were triggered from the startup notification toast
if ((std::wstring{ L"-from-startup-toast" }.compare(lpCmdLine) == 0))
{
return 0;
}
return Run(hInstance);
}
4 changes: 4 additions & 0 deletions src/FlashCom/pch.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <winrt/Microsoft.Graphics.Canvas.Geometry.h>
#include <winrt/Microsoft.Graphics.Canvas.Text.h>
#include <winrt/Microsoft.Graphics.Canvas.UI.Composition.h>
#include <winrt/Windows.Data.Xml.Dom.h>
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Graphics.DirectX.h>
#include <winrt/Windows.Graphics.Effects.h>
Expand All @@ -22,6 +23,7 @@
#include <winrt/Windows.UI.Composition.h>
#include <winrt/Windows.UI.Composition.Desktop.h>
#include <windows.ui.composition.interop.h>
#include <winrt/Windows.UI.Notifications.h>
#include <winrt/Windows.UI.Text.h>

// WIL
Expand Down Expand Up @@ -49,6 +51,7 @@ namespace winrt
namespace MGCG = Microsoft::Graphics::Canvas::Geometry;
namespace MGCT = Microsoft::Graphics::Canvas::Text;
namespace MGCUC = Microsoft::Graphics::Canvas::UI::Composition;
namespace WDXD = Windows::Data::Xml::Dom;
namespace WF = Windows::Foundation;
namespace WGDX = Windows::Graphics::DirectX;
namespace WStorage = Windows::Storage;
Expand All @@ -57,5 +60,6 @@ namespace winrt
namespace WUI = Windows::UI;
namespace WUIC = Windows::UI::Composition;
namespace WUICD = Windows::UI::Composition::Desktop;
namespace WUIN = Windows::UI::Notifications;
namespace WUIT = Windows::UI::Text;
}

0 comments on commit 42ea779

Please sign in to comment.