Skip to content

Commit 42ea779

Browse files
authored
🍞 Show toast notification on startup (#32)
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`.
1 parent 8ff8235 commit 42ea779

7 files changed

Lines changed: 81 additions & 11 deletions

File tree

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,13 @@ For the packaged app, `settings.json` is located in `%LOCALAPPDATA%\Packages\164
3939

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

42+
The following fields can be set in the `settings.json` root object:
43+
44+
| Name | Type | Description |
45+
| --------------------------- | ------------------------ | ------------------------------------------ |
46+
| `"showStartupNotification"` | `true` / `false` | Determines whether the "FlashCom is running" toast notification is shown on startup. |
47+
| `"commands"` | Array of Command Objects | The set of commands shown in FlashCom. |
48+
4249
## Commands
4350

4451
| Name | "type" | Description |

src/FlashCom/App.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,26 @@ namespace
99
{
1010
const std::unordered_set<uint32_t> c_hotkeyCombo{ VK_LWIN, VK_SPACE };
1111
constexpr std::chrono::milliseconds c_hotkeyExpirationTime{ 1000 };
12+
13+
void ShowStartupNotification()
14+
{
15+
winrt::WDXD::XmlDocument notificationPayload;
16+
notificationPayload.LoadXml(LR""""(
17+
<toast launch="-from-startup-toast">
18+
<visual>
19+
<binding template="ToastGeneric">
20+
<text>FlashCom is running!</text>
21+
<text>Press Win+Space to invoke.</text>
22+
<text>Manage settings from the system tray icon.</text>
23+
</binding>
24+
</visual>
25+
</toast>
26+
)"""");
27+
winrt::WUIN::ToastNotification notification{ notificationPayload };
28+
auto notificationManager{ winrt::WUIN::ToastNotificationManager::GetDefault() };
29+
auto notifier{ notificationManager.CreateToastNotifier() };
30+
notifier.Show(notification);
31+
}
1232
}
1333

1434
namespace FlashCom
@@ -24,6 +44,7 @@ namespace FlashCom
2444
void App::LoadDataModel()
2545
{
2646
auto result{ m_settingsManager.LoadSettings() };
47+
m_dataModel->ShowStartupNotification = m_settingsManager.GetShowStartupNotification();
2748
m_dataModel->RootNode = m_settingsManager.GetCommandTreeRoot();
2849
m_dataModel->CurrentNode = m_dataModel->RootNode.get();
2950
if (!result.has_value())
@@ -38,6 +59,15 @@ namespace FlashCom
3859

3960
int App::RunMessageLoop()
4061
{
62+
// On launch, notify the user that we are, in fact, running.
63+
// FlashCom has failed Windows Store certification in the past because
64+
// the tester thought the app didn't start successfully.
65+
if (m_dataModel->ShowStartupNotification)
66+
{
67+
SPDLOG_INFO("App::RunMessageLoop - Showing startup notification");
68+
ShowStartupNotification();
69+
}
70+
4171
MSG msg;
4272
while (GetMessage(&msg, nullptr, 0, 0))
4373
{

src/FlashCom/Models/DataModel.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ namespace FlashCom::Models
66
struct DataModel
77
{
88
std::string LoadErrorMessage;
9+
bool ShowStartupNotification;
910
std::shared_ptr<TreeNode> RootNode;
1011
TreeNode* CurrentNode{ nullptr };
1112
};

src/FlashCom/Settings/SettingsManager.cpp

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ namespace
1313
constexpr std::string_view c_appDataLocalDirectoryName{ "FlashCom" };
1414
constexpr std::string_view c_settingsFileName{ "settings.json" };
1515
// JSON property names
16+
constexpr std::string_view c_showStartupNotificationProperty{ "showStartupNotification" };
1617
constexpr std::string_view c_commandsJsonProperty{ "commands" };
1718
constexpr std::string_view c_commandNameJsonProperty{ "name" };
1819
constexpr std::string_view c_commandKeyJsonProperty{ "key" };
@@ -29,6 +30,7 @@ namespace
2930
constexpr std::string_view c_commandTypeValueAumid{ "aumid" };
3031
// Default settings.json contents
3132
constexpr std::string_view c_defaultSettingsFileContents{ R"({
33+
"showStartupNotification": true,
3234
"commands": [
3335
{
3436
"name": "README",
@@ -351,7 +353,7 @@ namespace FlashCom::Settings
351353
return std::unexpected(std::format("Could not parse settings.json file: {}",
352354
e.what()));
353355
}
354-
356+
PopulateSettingsValues(settingsLock, settingsJson);
355357
PopulateCommandTree(settingsLock, settingsJson);
356358
return {}; // Spurious warning C4715
357359
}
@@ -362,12 +364,37 @@ namespace FlashCom::Settings
362364
return m_settingsFilePath;
363365
}
364366

367+
bool SettingsManager::GetShowStartupNotification()
368+
{
369+
return m_showStartupNotification;
370+
}
371+
365372
std::shared_ptr<Models::TreeNode> SettingsManager::GetCommandTreeRoot()
366373
{
367374
std::shared_lock settingsLock{ m_settingsAccessMutex };
368375
return m_commandTreeRoot;
369376
}
370377

378+
void SettingsManager::PopulateSettingsValues(
379+
const std::unique_lock<std::shared_mutex>& /*accessLock*/,
380+
const nlohmann::json& settingsJson)
381+
{
382+
SPDLOG_INFO("SettingsManager::PopulateSettingsValues");
383+
384+
if (settingsJson.contains(c_showStartupNotificationProperty) &&
385+
settingsJson.at(c_showStartupNotificationProperty).is_boolean())
386+
{
387+
m_showStartupNotification =
388+
settingsJson.at(c_showStartupNotificationProperty).get<bool>();
389+
}
390+
else
391+
{
392+
SPDLOG_INFO("SettingsManager::PopulateSettingsValues - No '{}' bool property found. "
393+
"Defaulting to '{}'.", c_showStartupNotificationProperty,
394+
m_showStartupNotification);
395+
}
396+
}
397+
371398
std::expected<void, std::string> SettingsManager::PopulateCommandTree(
372399
const std::unique_lock<std::shared_mutex>& /*accessLock*/,
373400
const nlohmann::json& settingsJson)

src/FlashCom/Settings/SettingsManager.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,18 @@ namespace FlashCom::Settings
1313
SettingsManager();
1414
std::expected<void, std::string> LoadSettings();
1515
std::filesystem::path GetSettingsFilePath();
16+
bool GetShowStartupNotification();
1617
std::shared_ptr<Models::TreeNode> GetCommandTreeRoot();
1718

1819
private:
1920
std::filesystem::path const m_settingsFilePath;
2021
std::shared_mutex m_settingsAccessMutex;
22+
bool m_showStartupNotification{ true };
2123
std::shared_ptr<Models::TreeNode> m_commandTreeRoot;
2224

25+
void PopulateSettingsValues(
26+
const std::unique_lock<std::shared_mutex>& accessLock,
27+
const nlohmann::json& settingsJson);
2328
std::expected<void, std::string> PopulateCommandTree(
2429
const std::unique_lock<std::shared_mutex>& accessLock,
2530
const nlohmann::json& settingsJson);

src/FlashCom/main.cpp

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -40,22 +40,18 @@ namespace
4040
}
4141
}
4242

43-
// Console entrypoint
44-
int main(
45-
int /*argc*/,
46-
wchar_t* /*argv*/[]
47-
)
48-
{
49-
return Run(GetModuleHandleW(nullptr));
50-
}
51-
5243
// Windows entrypoint
5344
int WINAPI wWinMain(
5445
HINSTANCE hInstance,
5546
HINSTANCE /*hPrevInstance*/,
56-
LPWSTR /*lpCmdLine*/,
47+
LPWSTR lpCmdLine,
5748
int /*nCmdShow*/
5849
)
5950
{
51+
// Ignore launches that were triggered from the startup notification toast
52+
if ((std::wstring{ L"-from-startup-toast" }.compare(lpCmdLine) == 0))
53+
{
54+
return 0;
55+
}
6056
return Run(hInstance);
6157
}

src/FlashCom/pch.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <winrt/Microsoft.Graphics.Canvas.Geometry.h>
1414
#include <winrt/Microsoft.Graphics.Canvas.Text.h>
1515
#include <winrt/Microsoft.Graphics.Canvas.UI.Composition.h>
16+
#include <winrt/Windows.Data.Xml.Dom.h>
1617
#include <winrt/Windows.Foundation.h>
1718
#include <winrt/Windows.Graphics.DirectX.h>
1819
#include <winrt/Windows.Graphics.Effects.h>
@@ -22,6 +23,7 @@
2223
#include <winrt/Windows.UI.Composition.h>
2324
#include <winrt/Windows.UI.Composition.Desktop.h>
2425
#include <windows.ui.composition.interop.h>
26+
#include <winrt/Windows.UI.Notifications.h>
2527
#include <winrt/Windows.UI.Text.h>
2628

2729
// WIL
@@ -49,6 +51,7 @@ namespace winrt
4951
namespace MGCG = Microsoft::Graphics::Canvas::Geometry;
5052
namespace MGCT = Microsoft::Graphics::Canvas::Text;
5153
namespace MGCUC = Microsoft::Graphics::Canvas::UI::Composition;
54+
namespace WDXD = Windows::Data::Xml::Dom;
5255
namespace WF = Windows::Foundation;
5356
namespace WGDX = Windows::Graphics::DirectX;
5457
namespace WStorage = Windows::Storage;
@@ -57,5 +60,6 @@ namespace winrt
5760
namespace WUI = Windows::UI;
5861
namespace WUIC = Windows::UI::Composition;
5962
namespace WUICD = Windows::UI::Composition::Desktop;
63+
namespace WUIN = Windows::UI::Notifications;
6064
namespace WUIT = Windows::UI::Text;
6165
}

0 commit comments

Comments
 (0)