~haowenl/vorg-windows

7cbe35b4a8344786a5cbe0bf56f6a8bf03455bc6 — Haowen Liu 11 months ago 475675c
Implement BrowseDetailPage, rename various types for consistency
39 files changed, 1038 insertions(+), 320 deletions(-)

M libvorg
A todos.txt
M vorg-windows/App.xaml.cpp
M vorg-windows/App.xaml.h
M vorg-windows/BrowseDetailPage.idl
M vorg-windows/BrowseDetailPage.xaml
M vorg-windows/BrowseDetailPage.xaml.cpp
M vorg-windows/BrowseDetailPage.xaml.h
M vorg-windows/BrowseGalleryPage.idl
M vorg-windows/BrowseGalleryPage.xaml
M vorg-windows/BrowseGalleryPage.xaml.cpp
M vorg-windows/BrowseGalleryPage.xaml.h
M vorg-windows/BrowsePage.idl
M vorg-windows/BrowsePage.xaml
M vorg-windows/BrowsePage.xaml.cpp
M vorg-windows/BrowsePage.xaml.h
M vorg-windows/ImportEditPage.idl
M vorg-windows/ImportEditPage.xaml
M vorg-windows/ImportEditPage.xaml.cpp
M vorg-windows/ImportEditPage.xaml.h
M vorg-windows/ImportPage.xaml.cpp
M vorg-windows/MainWindow.xaml.cpp
M vorg-windows/MainWindow.xaml.h
M vorg-windows/VideoDataEditor.idl
M vorg-windows/VideoDataEditor.xaml
M vorg-windows/VideoDataEditor.xaml.cpp
M vorg-windows/VideoDataEditor.xaml.h
R vorg-windows/{VideoDataEditorState.cpp => VideoDetailData.cpp}
R vorg-windows/{VideoDataEditorState.h => VideoDetailData.h}
R vorg-windows/{VideoDataEditorState.idl => VideoDetailData.idl}
R vorg-windows/{BrowseVideoData.cpp => VideoGalleryData.cpp}
R vorg-windows/{BrowseVideoData.h => VideoGalleryData.h}
R vorg-windows/{BrowseVideoData.idl => VideoGalleryData.idl}
R vorg-windows/{ImportEditPageState.cpp => VideoPreviewData.cpp}
R vorg-windows/{ImportEditPageState.h => VideoPreviewData.h}
R vorg-windows/{ImportEditPageState.idl => VideoPreviewData.idl}
M vorg-windows/pch.h
M vorg-windows/vorg-windows.vcxproj
M vorg-windows/vorg-windows.vcxproj.filters
M libvorg => libvorg +1 -1
@@ 1,1 1,1 @@
Subproject commit bcca86300f2830ec117f7dae441bd151be400467
Subproject commit 864c48cf501eec071e53792afc8c50bee837666d

A todos.txt => todos.txt +2 -0
@@ 0,0 1,2 @@
1. Remove all direct use of ErrorContentDialog
2. Fix how unsubscribe is handled
\ No newline at end of file

M vorg-windows/App.xaml.cpp => vorg-windows/App.xaml.cpp +70 -5
@@ 2,8 2,10 @@

#include "App.xaml.h"

#include "BrowseVideoData.h"
#include "MainWindow.xaml.h"
#include "VideoDetailData.h"
#include "VideoGalleryData.h"
#include "VideoPreviewData.h"

using namespace winrt;
using namespace Windows::Foundation;


@@ 100,21 102,23 @@ void App::CancelImport()
    mRepo->cancelImport();
}

void App::PopulateVideoData(
void App::PopulateGalleryData(
    Windows::Foundation::Collections::IObservableVector<Windows::Foundation::IInspectable> &resultData) const
{
    resultData.Clear();
    std::vector<Vorg::VorgFileData> rawData = mRepo->getFiles(mFilter);
    for (const Vorg::VorgFileData &fileData : rawData)
    {
        vorg_windows::BrowseVideoData winrtData = make<vorg_windows::implementation::BrowseVideoData>();
        vorg_windows::VideoGalleryData winrtData = make<vorg_windows::implementation::VideoGalleryData>();

        std::filesystem::path videoPathUTF8 = mRepo->getVideoPath(fileData);
        std::vector<std::filesystem::path> thumbnailPathsUTF8 = mRepo->getThumbnailPaths(fileData);
        std::filesystem::path videoPathUTF8 = mRepo->getVideoPath(fileData.getHash(), fileData.getExt());
        std::vector<std::filesystem::path> thumbnailPathsUTF8 = mRepo->getThumbnailPaths(fileData.getHash());

        hstring hash = to_hstring(fileData.getHash());
        hstring videoPath = to_hstring(videoPathUTF8.string());
        // NOTE: currently only the first thumbnail at 25% is used
        hstring thumbnailPath = to_hstring(thumbnailPathsUTF8[0].string());
        winrtData.Hash(hash);
        winrtData.VideoPath(videoPath);
        winrtData.ThumbnailPath(thumbnailPath);



@@ 122,6 126,67 @@ void App::PopulateVideoData(
    }
}

void App::GetVideoDetail(const hstring &hash, vorg_windows::VideoDetailData &detailData,
                         vorg_windows::VideoPreviewData &previewData) const
{
    std::string hashUTF8 = to_string(hash);
    std::optional<Vorg::VorgFileData> rawData = mRepo->getFile(hashUTF8);
    if (rawData.has_value())
    {
        detailData = make<vorg_windows::implementation::VideoDetailData>();
        previewData = make<vorg_windows::implementation::VideoPreviewData>();

        detailData.Hash(hash);

        hstring title = to_hstring(rawData->getTitle());
        detailData.Title(title);
        previewData.FileName(title);

        previewData.VideoPath(to_hstring(mRepo->getVideoPath(rawData->getHash(), rawData->getExt()).string()));

        detailData.Studio(to_hstring(rawData->getStudio()));

        Windows::Foundation::Collections::IObservableVector<hstring> actors =
            single_threaded_observable_vector<hstring>();
        Windows::Foundation::Collections::IObservableVector<hstring> tags =
            single_threaded_observable_vector<hstring>();
        for (auto &actor : rawData->getActors())
        {
            actors.Append(to_hstring(actor));
        }
        for (auto &tag : rawData->getTags())
        {
            tags.Append(to_hstring(tag));
        }
        detailData.Actors(actors);
        detailData.Tags(tags);
    }
}

void App::DeleteVideo(const hstring &hash)
{
    mRepo->deleteFile(to_string(hash));
}

void App::EditVideo(const hstring &hash, const hstring &title, const hstring &studio,
                    const Windows::Foundation::Collections::IObservableVector<hstring> &actors,
                    const Windows::Foundation::Collections::IObservableVector<hstring> &tags)
{
    std::string hashUTF8 = to_string(hash);
    std::optional<Vorg::VorgFileData> originalData = mRepo->getFile(hashUTF8);
    if (!originalData.has_value())
        return;

    std::string titleUTF8 = to_string(title);
    std::string studioUTF8 = to_string(studio);
    std::vector<std::string> actorsStd = WinrtToStdVector(actors);
    std::vector<std::string> tagsStd = WinrtToStdVector(tags);

    Vorg::VorgFileData editData(hashUTF8, titleUTF8, originalData->getExt(), studioUTF8, actorsStd, tagsStd);

    mRepo->editFile(editData);
}

Windows::Foundation::Collections::IVector<Windows::Foundation::IInspectable> App::CompleteStudios(
    const hstring &studio) const
{

M vorg-windows/App.xaml.h => vorg-windows/App.xaml.h +7 -1
@@ 23,8 23,14 @@ struct App : AppT<App>
                    const Windows::Foundation::Collections::IObservableVector<hstring> &tags);
    bool Skip();
    void CancelImport();
    void PopulateVideoData(
    void PopulateGalleryData(
        Windows::Foundation::Collections::IObservableVector<Windows::Foundation::IInspectable> &resultData) const;
    void GetVideoDetail(const hstring &hash, vorg_windows::VideoDetailData &detailData,
                        vorg_windows::VideoPreviewData &previewData) const;
    void DeleteVideo(const hstring &hash);
    void EditVideo(const hstring &hash, const hstring &title, const hstring &studio,
                   const Windows::Foundation::Collections::IObservableVector<hstring> &actors,
                   const Windows::Foundation::Collections::IObservableVector<hstring> &tags);
    Windows::Foundation::Collections::IVector<Windows::Foundation::IInspectable> CompleteStudios(
        const hstring &studio) const;
    Windows::Foundation::Collections::IVector<Windows::Foundation::IInspectable> CompleteActors(

M vorg-windows/BrowseDetailPage.idl => vorg-windows/BrowseDetailPage.idl +19 -7
@@ 1,12 1,24 @@
// Copyright (c) Microsoft Corporation and Contributors.
// Licensed under the MIT License.
import "VideoDetailData.idl";
import "VideoPreviewData.idl";

namespace vorg_windows
{
    [default_interface]
    runtimeclass BrowseDetailPage : Microsoft.UI.Xaml.Controls.Page
[default_interface] runtimeclass BrowseDetailPage : Microsoft.UI.Xaml.Controls.Page
{
    BrowseDetailPage();

    VideoDetailData DetailData
    {
        get;
        set;
    };
    VideoPreviewData PreviewData
    {
        BrowseDetailPage();
        Int32 MyProperty;
    }
        get;
        set;
    };
    
    event Windows.Foundation.TypedEventHandler<BrowseDetailPage, Object> BackFromDetail;
    event Windows.Foundation.TypedEventHandler<BrowseDetailPage, Object> VideoModified;
}
} // namespace vorg_windows

M vorg-windows/BrowseDetailPage.xaml => vorg-windows/BrowseDetailPage.xaml +169 -7
@@ 1,16 1,178 @@
<!-- Copyright (c) Microsoft Corporation and Contributors. -->
<!-- Licensed under the MIT License. -->

<Page
    x:Class="vorg_windows.BrowseDetailPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:vorg_windows"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:local="using:vorg_windows"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center">
        <Button x:Name="myButton" Click="myButton_Click">Click Me</Button>
    </StackPanel>
    <Grid
        Margin="0,44,0,44"
        HorizontalAlignment="Stretch"
        VerticalAlignment="Stretch">
        <Grid
            Height="40"
            Margin="0,0,0,22"
            VerticalAlignment="Top"
            Background="Transparent">
            <Button x:Name="BackButton" Click="BackButton_Click">
                <StackPanel Orientation="Horizontal" Spacing="8">
                    <FontIcon
                        FontFamily="Segoe Fluent Icons"
                        FontSize="16"
                        Glyph="&#xE72B;" />
                    <TextBlock>Back</TextBlock>
                </StackPanel>
            </Button>
        </Grid>

        <UserControl x:Name="UserControl">

            <StackPanel Margin="0,62,0,0" Orientation="Vertical">
                <TextBlock
                    Margin="0,0,0,20"
                    VerticalAlignment="Center"
                    FontSize="22"
                    Style="{StaticResource SubtitleTextBlockStyle}"
                    Text="{x:Bind Path=PreviewData.FileName, Mode=OneWay}" />
                <MediaPlayerElement
                    x:Name="MediaPlayer"
                    AreTransportControlsEnabled="True"
                    BorderThickness="0"
                    CornerRadius="16"
                    Source="{x:Bind Path=PreviewData.MediaSource, Mode=OneWay}" />

                <Grid>
                    <StackPanel
                        x:Name="DataDisplay"
                        Margin="0,16,0,0"
                        Orientation="Vertical"
                        Spacing="8">
                        <!--  Title  -->
                        <StackPanel Orientation="Horizontal" Spacing="8">
                            <TextBlock Style="{StaticResource BodyStrongTextBlockStyle}">Title:</TextBlock>
                            <TextBlock Text="{x:Bind DetailData.Title, Mode=OneWay}" />
                        </StackPanel>
                        <!--  Studio  -->
                        <StackPanel Orientation="Horizontal" Spacing="8">
                            <TextBlock Style="{StaticResource BodyStrongTextBlockStyle}">Studio:</TextBlock>
                            <TextBlock Text="{x:Bind DetailData.Studio, Mode=OneWay}" />
                        </StackPanel>
                        <!--  Actors  -->
                        <StackPanel Orientation="Horizontal" Spacing="8">
                            <TextBlock Style="{StaticResource BodyStrongTextBlockStyle}">Actor(s):</TextBlock>
                            <TextBlock x:Name="ActorsNameBlock" />
                        </StackPanel>
                        <!--  Tags  -->
                        <StackPanel Orientation="Horizontal" Spacing="8">
                            <TextBlock Style="{StaticResource BodyStrongTextBlockStyle}">Tag(s):</TextBlock>
                            <TextBlock x:Name="TagsNameBlock" />
                        </StackPanel>
                    </StackPanel>

                    <local:VideoDataEditor
                        x:Name="DataEditor"
                        ActorTextChanged="VideoDataEditor_ActorTextChanged"
                        StudioTextChanged="VideoDataEditor_StudioTextChanged"
                        TagTextChanged="VideoDataEditor_TagTextChanged"
                        Visibility="Collapsed" />
                </Grid>

                <Grid Margin="0,24,0,0">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition />
                        <ColumnDefinition />
                    </Grid.ColumnDefinitions>

                    <StackPanel
                        x:Name="DeleteControl"
                        Grid.Column="0"
                        HorizontalAlignment="Left"
                        Orientation="Horizontal"
                        Spacing="16">
                        <Button
                            x:Name="DeleteButton"
                            Width="96"
                            Click="DeleteButton_Click">
                            <StackPanel Orientation="Horizontal" Spacing="8">
                                <FontIcon FontFamily="Segoe Fluent Icons" Glyph="&#xe74d;" />
                                <TextBlock>Delete</TextBlock>
                            </StackPanel>
                        </Button>
                    </StackPanel>

                    <StackPanel
                        x:Name="DisplayControl"
                        Grid.Column="1"
                        HorizontalAlignment="Right"
                        Orientation="Horizontal"
                        Spacing="16">
                        <Button
                            x:Name="EditButton"
                            Width="96"
                            Click="EditButton_Click">
                            <StackPanel Orientation="Horizontal" Spacing="8">
                                <FontIcon FontFamily="Segoe Fluent Icons" Glyph="&#xe70f;" />
                                <TextBlock>Edit</TextBlock>
                            </StackPanel>
                        </Button>
                        <Button
                            x:Name="OpenButton"
                            Width="96"
                            Click="OpenButton_Click"
                            Style="{StaticResource AccentButtonStyle}">
                            <StackPanel Orientation="Horizontal" Spacing="8">
                                <FontIcon FontFamily="Segoe Fluent Icons" Glyph="&#xe768;" />
                                <TextBlock>Open</TextBlock>
                            </StackPanel>
                        </Button>
                    </StackPanel>

                    <StackPanel
                        x:Name="EditorControl"
                        Grid.Column="1"
                        HorizontalAlignment="Right"
                        Orientation="Horizontal"
                        Spacing="16"
                        Visibility="Collapsed">
                        <Button
                            x:Name="CancelEditButton"
                            Width="96"
                            Click="CancelEditButton_Click">
                            <StackPanel Orientation="Horizontal" Spacing="8">
                                <FontIcon FontFamily="Segoe Fluent Icons" Glyph="&#xe711;" />
                                <TextBlock>Cancel</TextBlock>
                            </StackPanel>
                        </Button>
                        <Button
                            x:Name="SaveButton"
                            Width="96"
                            Click="SaveButton_Click"
                            Style="{StaticResource AccentButtonStyle}">
                            <StackPanel Orientation="Horizontal" Spacing="8">
                                <FontIcon FontFamily="Segoe Fluent Icons" Glyph="&#xe74e;" />
                                <TextBlock>Save</TextBlock>
                            </StackPanel>
                        </Button>
                    </StackPanel>
                </Grid>

                <VisualStateManager.VisualStateGroups>
                    <VisualStateGroup>
                        <VisualState x:Name="EditorHidden" />
                        <VisualState x:Name="EditorShown">
                            <VisualState.Setters>
                                <Setter Target="DeleteControl.Visibility" Value="Collapsed" />
                                <Setter Target="EditorControl.Visibility" Value="Visible" />
                                <Setter Target="DisplayControl.Visibility" Value="Collapsed" />
                                <Setter Target="DataEditor.Visibility" Value="Visible" />
                                <Setter Target="DataDisplay.Visibility" Value="Collapsed" />
                            </VisualState.Setters>
                        </VisualState>
                    </VisualStateGroup>
                </VisualStateManager.VisualStateGroups>
            </StackPanel>
        </UserControl>
    </Grid>
</Page>

M vorg-windows/BrowseDetailPage.xaml.cpp => vorg-windows/BrowseDetailPage.xaml.cpp +216 -14
@@ 1,37 1,239 @@
// Copyright (c) Microsoft Corporation and Contributors.
// Licensed under the MIT License.

#include "pch.h"

#include "BrowseDetailPage.xaml.h"
#if __has_include("BrowseDetailPage.g.cpp")
#include "BrowseDetailPage.g.cpp"
#endif

#include "VideoDetailData.h"

using namespace winrt;
using namespace Microsoft::UI::Xaml;

// To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info.

namespace winrt::vorg_windows::implementation
{
    BrowseDetailPage::BrowseDetailPage()
BrowseDetailPage::BrowseDetailPage()
{
    InitializeComponent();

    // Initialize app ptr
    mApp.copy_from(get_self<vorg_windows::implementation::App>(
        Application::Current().as<default_interface<vorg_windows::implementation::App>>()));

    // Make a separate copy for editor
    vorg_windows::VideoDetailData detailDataCopy = make<vorg_windows::implementation::VideoDetailData>();
    DataEditor().DetailData(detailDataCopy);
}

BrowseDetailPage::~BrowseDetailPage()
{
    mDetailData.Actors().VectorChanged(mActorsVectorChangedToken);
    mDetailData.Tags().VectorChanged(mTagsVectorChangedToken);
    mDetailData.PropertyChanged(mDetailDataPropertyChangedToken);
}

vorg_windows::VideoDetailData BrowseDetailPage::DetailData() const
{
    return mDetailData;
}

void BrowseDetailPage::DetailData(const vorg_windows::VideoDetailData &detailData)
{
    mDetailData = detailData;
    // Subscribe from update events
    mActorsVectorChangedToken = detailData.Actors().VectorChanged([this](auto &, auto &) { UpdateActorsTextBlock(); });
    mTagsVectorChangedToken = detailData.Tags().VectorChanged([this](auto &, auto &) { UpdateTagsTextBlock(); });
    mDetailDataPropertyChangedToken = detailData.PropertyChanged([this](auto &, auto &args) {
        if (args.PropertyName() == L"Actors")
        {
            UpdateActorsTextBlock();
        }
        else if (args.PropertyName() == L"Tags")
        {
            UpdateTagsTextBlock();
        }
    });
    // Update now
    UpdateActorsTextBlock();
    UpdateTagsTextBlock();

    // Keep a copy of detailData in DataEditor
    CopyDataToEditor();
}

vorg_windows::VideoPreviewData BrowseDetailPage::PreviewData() const
{
    return mPreviewData;
}

void BrowseDetailPage::PreviewData(const vorg_windows::VideoPreviewData &previewData)
{
    mPreviewData = previewData;
}

event_token BrowseDetailPage::BackFromDetail(
    const Windows::Foundation::TypedEventHandler<vorg_windows::BrowseDetailPage, Windows::Foundation::IInspectable>
        &handler)
{
    return mBackFromDetailEvent.add(handler);
}

void BrowseDetailPage::BackFromDetail(const event_token &token)
{
    mBackFromDetailEvent.remove(token);
}

event_token BrowseDetailPage::VideoModified(
    const Windows::Foundation::TypedEventHandler<vorg_windows::BrowseDetailPage, Windows::Foundation::IInspectable>
        &handler)
{
    return mVideoModifiedEvent.add(handler);
}

void BrowseDetailPage::VideoModified(const event_token &token)
{
    mVideoModifiedEvent.remove(token);
}

void BrowseDetailPage::BackButton_Click(const Windows::Foundation::IInspectable &,
                                        const Microsoft::UI::Xaml::RoutedEventArgs &)
{
    mBackFromDetailEvent(*this, nullptr);
}

fire_and_forget BrowseDetailPage::OpenButton_Click(const Windows::Foundation::IInspectable &,
                                                   const Microsoft::UI::Xaml::RoutedEventArgs &)
{
    Windows::Storage::StorageFile videoFile =
        co_await Windows::Storage::StorageFile::GetFileFromPathAsync(mPreviewData.VideoPath());
    if (videoFile)
    {
        Windows::System::Launcher::LaunchFileAsync(videoFile);
    }
    else
    {
        vorg_windows::ErrorContentDialog dialog;
        dialog.Title(box_value(L"Error opening file"));
        dialog.Content(box_value(L"This should not happen. Please file a bug report."));
        dialog.XamlRoot(XamlRoot());
        co_await dialog.ShowAsync();
    }
}

void BrowseDetailPage::EditButton_Click(const Windows::Foundation::IInspectable &,
                                        const Microsoft::UI::Xaml::RoutedEventArgs &)
{
    VisualStateManager::GoToState(UserControl(), L"EditorShown", false);
}

void BrowseDetailPage::DeleteButton_Click(const Windows::Foundation::IInspectable &,
                                          const Microsoft::UI::Xaml::RoutedEventArgs &)
{
    mApp->DeleteVideo(mDetailData.Hash());
    mVideoModifiedEvent(*this, nullptr);
    mBackFromDetailEvent(*this, nullptr);
}

void BrowseDetailPage::CancelEditButton_Click(const Windows::Foundation::IInspectable &,
                                              const Microsoft::UI::Xaml::RoutedEventArgs &)
{
    // Go back to display mode
    VisualStateManager::GoToState(UserControl(), L"EditorHidden", false);

    // Discard editor data
    CopyDataToEditor();
}

void BrowseDetailPage::SaveButton_Click(const Windows::Foundation::IInspectable &,
                                        const Microsoft::UI::Xaml::RoutedEventArgs &)
{
    DataEditor().AddAllCurrent();

    mApp->EditVideo(mDetailData.Hash(), DataEditor().DetailData().Title(), DataEditor().DetailData().Studio(),
                    DataEditor().DetailData().Actors(), DataEditor().DetailData().Tags());

    // Copy data back to display
    mDetailData.Actors().Clear();
    for (uint32_t i = 0; i < DataEditor().DetailData().Actors().Size(); ++i)
    {
        mDetailData.Actors().Append(DataEditor().DetailData().Actors().GetAt(i));
    }

    mDetailData.Tags().Clear();
    for (uint32_t i = 0; i < DataEditor().DetailData().Tags().Size(); ++i)
    {
        mDetailData.Tags().Append(DataEditor().DetailData().Tags().GetAt(i));
    }

    mDetailData.Title(DataEditor().DetailData().Title() + L"");
    mDetailData.Studio(DataEditor().DetailData().Studio() + L"");

    // Go back to display mode
    VisualStateManager::GoToState(UserControl(), L"EditorHidden", false);
}

void BrowseDetailPage::VideoDataEditor_StudioTextChanged(
    const winrt::Microsoft::UI::Xaml::Controls::AutoSuggestBox &sender,
    const winrt::Microsoft::UI::Xaml::Controls::AutoSuggestBoxTextChangedEventArgs &)
{
    sender.ItemsSource(mApp->CompleteStudios(sender.Text()));
}

void BrowseDetailPage::VideoDataEditor_ActorTextChanged(
    const winrt::Microsoft::UI::Xaml::Controls::AutoSuggestBox &sender,
    const winrt::Microsoft::UI::Xaml::Controls::AutoSuggestBoxTextChangedEventArgs &)
{
    sender.ItemsSource(mApp->CompleteActors(sender.Text()));
}

void BrowseDetailPage::VideoDataEditor_TagTextChanged(
    const winrt::Microsoft::UI::Xaml::Controls::AutoSuggestBox &sender,
    const winrt::Microsoft::UI::Xaml::Controls::AutoSuggestBoxTextChangedEventArgs &)
{
    sender.ItemsSource(mApp->CompleteTags(sender.Text()));
}

void BrowseDetailPage::UpdateActorsTextBlock()
{
    std::wstring actorsDisplay;
    uint32_t n = mDetailData.Actors().Size();
    for (uint32_t i = 0; i < n; ++i)
    {
        InitializeComponent();
        actorsDisplay += mDetailData.Actors().GetAt(i);
        if (i != n - 1)
            actorsDisplay += L", ";
    }
    ActorsNameBlock().Text(actorsDisplay);
}

    int32_t BrowseDetailPage::MyProperty()
void BrowseDetailPage::UpdateTagsTextBlock()
{
    std::wstring tagsDisplay;
    uint32_t n = mDetailData.Tags().Size();
    for (uint32_t i = 0; i < n; ++i)
    {
        throw hresult_not_implemented();
        tagsDisplay += mDetailData.Tags().GetAt(i);
        if (i != n - 1)
            tagsDisplay += L", ";
    }
    TagsNameBlock().Text(tagsDisplay);
}

    void BrowseDetailPage::MyProperty(int32_t /* value */)
void BrowseDetailPage::CopyDataToEditor()
{
    DataEditor().DetailData().Actors().Clear();
    for (uint32_t i = 0; i < mDetailData.Actors().Size(); ++i)
    {
        throw hresult_not_implemented();
        DataEditor().DetailData().Actors().Append(mDetailData.Actors().GetAt(i));
    }

    void BrowseDetailPage::myButton_Click(IInspectable const&, RoutedEventArgs const&)
    DataEditor().DetailData().Tags().Clear();
    for (uint32_t i = 0; i < mDetailData.Tags().Size(); ++i)
    {
        myButton().Content(box_value(L"Clicked"));
        DataEditor().DetailData().Tags().Append(mDetailData.Tags().GetAt(i));
    }

    DataEditor().DetailData().Title(mDetailData.Title() + L"");
    DataEditor().DetailData().Studio(mDetailData.Studio() + L"");
}
} // namespace winrt::vorg_windows::implementation

M vorg-windows/BrowseDetailPage.xaml.h => vorg-windows/BrowseDetailPage.xaml.h +64 -15
@@ 1,26 1,75 @@
// Copyright (c) Microsoft Corporation and Contributors.
// Licensed under the MIT License.

#pragma once

#include "App.xaml.h"
#include "BrowseDetailPage.g.h"

namespace winrt::vorg_windows::implementation
{
    struct BrowseDetailPage : BrowseDetailPageT<BrowseDetailPage>
    {
        BrowseDetailPage();
struct BrowseDetailPage : BrowseDetailPageT<BrowseDetailPage>
{
    BrowseDetailPage();
    ~BrowseDetailPage();

    // Properties
    vorg_windows::VideoDetailData DetailData() const;
    void DetailData(const vorg_windows::VideoDetailData &detailData);

    vorg_windows::VideoPreviewData PreviewData() const;
    void PreviewData(const vorg_windows::VideoPreviewData &previewData);

    // Events
    event_token BackFromDetail(
        const Windows::Foundation::TypedEventHandler<vorg_windows::BrowseDetailPage, Windows::Foundation::IInspectable>
            &handler);
    void BackFromDetail(const event_token &token);

    event_token VideoModified(const Windows::Foundation::TypedEventHandler<vorg_windows::BrowseDetailPage,
                                                                          Windows::Foundation::IInspectable> &handler);
    void VideoModified(const event_token &token);

        int32_t MyProperty();
        void MyProperty(int32_t value);
    // Handlers
    void BackButton_Click(const Windows::Foundation::IInspectable &, const Microsoft::UI::Xaml::RoutedEventArgs &);
    fire_and_forget OpenButton_Click(const Windows::Foundation::IInspectable &,
                                     const Microsoft::UI::Xaml::RoutedEventArgs &);
    void EditButton_Click(const Windows::Foundation::IInspectable &, const Microsoft::UI::Xaml::RoutedEventArgs &);
    void DeleteButton_Click(const Windows::Foundation::IInspectable &, const Microsoft::UI::Xaml::RoutedEventArgs &);
    void CancelEditButton_Click(const Windows::Foundation::IInspectable &, const Microsoft::UI::Xaml::RoutedEventArgs &);
    void SaveButton_Click(const Windows::Foundation::IInspectable &, const Microsoft::UI::Xaml::RoutedEventArgs &);

        void myButton_Click(Windows::Foundation::IInspectable const& sender, Microsoft::UI::Xaml::RoutedEventArgs const& args);
    };
}
    void VideoDataEditor_StudioTextChanged(const Microsoft::UI::Xaml::Controls::AutoSuggestBox &sender,
                                           const Microsoft::UI::Xaml::Controls::AutoSuggestBoxTextChangedEventArgs &);
    void VideoDataEditor_ActorTextChanged(const Microsoft::UI::Xaml::Controls::AutoSuggestBox &sender,
                                          const Microsoft::UI::Xaml::Controls::AutoSuggestBoxTextChangedEventArgs &);
    void VideoDataEditor_TagTextChanged(const Microsoft::UI::Xaml::Controls::AutoSuggestBox &sender,
                                        const Microsoft::UI::Xaml::Controls::AutoSuggestBoxTextChangedEventArgs &);

  private:
    // Properties
    vorg_windows::VideoDetailData mDetailData{nullptr};
    vorg_windows::VideoPreviewData mPreviewData{nullptr};

    // Events
    event<Windows::Foundation::TypedEventHandler<vorg_windows::BrowseDetailPage, Windows::Foundation::IInspectable>>
        mBackFromDetailEvent;
    event<Windows::Foundation::TypedEventHandler<vorg_windows::BrowseDetailPage, Windows::Foundation::IInspectable>>
        mVideoModifiedEvent;

    // Members
    event_token mDetailDataPropertyChangedToken;
    event_token mActorsVectorChangedToken;
    event_token mTagsVectorChangedToken;
    com_ptr<vorg_windows::implementation::App> mApp;

    // Helper functions
    void UpdateActorsTextBlock();
    void UpdateTagsTextBlock();
    void CopyDataToEditor();
};
} // namespace winrt::vorg_windows::implementation

namespace winrt::vorg_windows::factory_implementation
{
    struct BrowseDetailPage : BrowseDetailPageT<BrowseDetailPage, implementation::BrowseDetailPage>
    {
    };
}
struct BrowseDetailPage : BrowseDetailPageT<BrowseDetailPage, implementation::BrowseDetailPage>
{
};
} // namespace winrt::vorg_windows::factory_implementation

M vorg-windows/BrowseGalleryPage.idl => vorg-windows/BrowseGalleryPage.idl +11 -8
@@ 1,12 1,15 @@
// Copyright (c) Microsoft Corporation and Contributors.
// Licensed under the MIT License.

namespace vorg_windows
{
    [default_interface]
    runtimeclass BrowseGalleryPage : Microsoft.UI.Xaml.Controls.Page
[default_interface] runtimeclass BrowseGalleryPage : Microsoft.UI.Xaml.Controls.Page
{
    BrowseGalleryPage();

    Windows.Foundation.Collections.IObservableVector<Object> GalleryData
    {
        BrowseGalleryPage();
        Int32 MyProperty;
    }
        get;
        set;
    };

    event Windows.Foundation.TypedEventHandler<BrowseGalleryPage, String> ShowVideoDetail;
}
} // namespace vorg_windows

M vorg-windows/BrowseGalleryPage.xaml => vorg-windows/BrowseGalleryPage.xaml +36 -5
@@ 1,16 1,47 @@
<!-- Copyright (c) Microsoft Corporation and Contributors. -->
<!-- Licensed under the MIT License. -->
<!--  Copyright (c) Microsoft Corporation and Contributors.  -->
<!--  Licensed under the MIT License.  -->

<Page
    x:Class="vorg_windows.BrowseGalleryPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:vorg_windows"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:local="using:vorg_windows"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center">
        <Button x:Name="myButton" Click="myButton_Click">Click Me</Button>
    <Page.Resources>
        <DataTemplate x:Key="ThumbnailTemplate" x:DataType="local:VideoGalleryData">
            <Image
                Width="190"
                Height="130"
                DoubleTapped="Thumbnail_DoubleTapped"
                IsDoubleTapEnabled="True"
                Source="{x:Bind ThumbnailPath}"
                Stretch="UniformToFill" />
        </DataTemplate>
    </Page.Resources>

    <StackPanel
        Margin="0,44,0,44"
        HorizontalAlignment="Stretch"
        VerticalAlignment="Stretch">
        <Grid
            Height="40"
            Margin="0,0,0,22"
            VerticalAlignment="Top"
            Background="Transparent">
            <TextBlock
                VerticalAlignment="Center"
                FontSize="22"
                Style="{StaticResource TitleTextBlockStyle}"
                Text="Browse Repository" />
        </Grid>

        <GridView
            x:Name="VideoGallery"
            ItemTemplate="{StaticResource ThumbnailTemplate}"
            ItemsSource="{x:Bind GalleryData, Mode=OneWay}"
            SelectionMode="Single" />
    </StackPanel>
</Page>

M vorg-windows/BrowseGalleryPage.xaml.cpp => vorg-windows/BrowseGalleryPage.xaml.cpp +37 -25
@@ 1,7 1,5 @@
// Copyright (c) Microsoft Corporation and Contributors.
// Licensed under the MIT License.

#include "pch.h"

#include "BrowseGalleryPage.xaml.h"
#if __has_include("BrowseGalleryPage.g.cpp")
#include "BrowseGalleryPage.g.cpp"


@@ 10,28 8,42 @@
using namespace winrt;
using namespace Microsoft::UI::Xaml;

// To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info.

namespace winrt::vorg_windows::implementation
{
    BrowseGalleryPage::BrowseGalleryPage()
    {
        InitializeComponent();
    }

    int32_t BrowseGalleryPage::MyProperty()
    {
        throw hresult_not_implemented();
    }

    void BrowseGalleryPage::MyProperty(int32_t /* value */)
    {
        throw hresult_not_implemented();
    }

    void BrowseGalleryPage::myButton_Click(IInspectable const&, RoutedEventArgs const&)
    {
        myButton().Content(box_value(L"Clicked"));
    }
BrowseGalleryPage::BrowseGalleryPage()
{
    InitializeComponent();
}

Windows::Foundation::Collections::IObservableVector<Windows::Foundation::IInspectable> BrowseGalleryPage::GalleryData()
    const
{
    return mGalleryData;
}

void BrowseGalleryPage::GalleryData(
    const Windows::Foundation::Collections::IObservableVector<Windows::Foundation::IInspectable> &videoData)
{
    mGalleryData = videoData;
}

event_token BrowseGalleryPage::ShowVideoDetail(
    const Windows::Foundation::TypedEventHandler<vorg_windows::BrowseGalleryPage, hstring> &handler)
{
    return mShowVideoDetailEvent.add(handler);
}

void BrowseGalleryPage::ShowVideoDetail(const event_token &token)
{
    mShowVideoDetailEvent.remove(token);
}

void BrowseGalleryPage::Thumbnail_DoubleTapped(Windows::Foundation::IInspectable const &sender,
                                               Microsoft::UI::Xaml::Input::DoubleTappedRoutedEventArgs const &)
{
    Microsoft::UI::Xaml::Controls::Image image = sender.as<Microsoft::UI::Xaml::Controls::Image>();
    Windows::Foundation::IInspectable dataContext = image.DataContext();
    vorg_windows::VideoGalleryData videoData = dataContext.as<vorg_windows::VideoGalleryData>();
    mShowVideoDetailEvent(*this, videoData.Hash());
}
} // namespace winrt::vorg_windows::implementation

M vorg-windows/BrowseGalleryPage.xaml.h => vorg-windows/BrowseGalleryPage.xaml.h +28 -15
@@ 1,26 1,39 @@
// Copyright (c) Microsoft Corporation and Contributors.
// Licensed under the MIT License.

#pragma once

#include "BrowseGalleryPage.g.h"

namespace winrt::vorg_windows::implementation
{
    struct BrowseGalleryPage : BrowseGalleryPageT<BrowseGalleryPage>
    {
        BrowseGalleryPage();
struct BrowseGalleryPage : BrowseGalleryPageT<BrowseGalleryPage>
{
    BrowseGalleryPage();

    // Properties
    Windows::Foundation::Collections::IObservableVector<Windows::Foundation::IInspectable> GalleryData() const;
    void GalleryData(
        const Windows::Foundation::Collections::IObservableVector<Windows::Foundation::IInspectable> &videoData);

        int32_t MyProperty();
        void MyProperty(int32_t value);
    // Events
    event_token ShowVideoDetail(
        const Windows::Foundation::TypedEventHandler<vorg_windows::BrowseGalleryPage, hstring> &handler);
    void ShowVideoDetail(const event_token &token);

        void myButton_Click(Windows::Foundation::IInspectable const& sender, Microsoft::UI::Xaml::RoutedEventArgs const& args);
    };
}
    // Handlers
    void Thumbnail_DoubleTapped(Windows::Foundation::IInspectable const &sender,
                                           Microsoft::UI::Xaml::Input::DoubleTappedRoutedEventArgs const &);

  private:
    // Properties
    Windows::Foundation::Collections::IObservableVector<Windows::Foundation::IInspectable> mGalleryData;

    // Events
    event<Windows::Foundation::TypedEventHandler<vorg_windows::BrowseGalleryPage, hstring>> mShowVideoDetailEvent;
};
} // namespace winrt::vorg_windows::implementation

namespace winrt::vorg_windows::factory_implementation
{
    struct BrowseGalleryPage : BrowseGalleryPageT<BrowseGalleryPage, implementation::BrowseGalleryPage>
    {
    };
}
struct BrowseGalleryPage : BrowseGalleryPageT<BrowseGalleryPage, implementation::BrowseGalleryPage>
{
};
} // namespace winrt::vorg_windows::factory_implementation

M vorg-windows/BrowsePage.idl => vorg-windows/BrowsePage.idl +3 -6
@@ 1,18 1,15 @@
// Copyright (c) Microsoft Corporation and Contributors.
// Licensed under the MIT License.

import "BrowseVideoData.idl";

namespace vorg_windows
{
[default_interface] runtimeclass BrowsePage : Microsoft.UI.Xaml.Controls.Page
{
    BrowsePage();

    Windows.Foundation.Collections.IObservableVector<Object> VideoData
    Windows.Foundation.Collections.IObservableVector<Object> GalleryData
    {
        get;
        set;
    };

    event Windows.Foundation.TypedEventHandler<BrowsePage, Object> VideoModified;
}
} // namespace vorg_windows

M vorg-windows/BrowsePage.xaml => vorg-windows/BrowsePage.xaml +7 -34
@@ 11,38 11,11 @@
    NavigationCacheMode="Required"
    mc:Ignorable="d">

    <Page.Resources>
        <DataTemplate x:Key="ThumbnailTemplate" x:DataType="local:BrowseVideoData">
            <Image
                Width="190"
                Height="130"
                DoubleTapped="Thumbnail_DoubleTapped"
                IsDoubleTapEnabled="True"
                Source="{x:Bind ThumbnailPath}"
                Stretch="UniformToFill" />
        </DataTemplate>
    </Page.Resources>

    <StackPanel
        Margin="0,44,0,44"
        HorizontalAlignment="Stretch"
        VerticalAlignment="Stretch">
        <Grid
            Height="40"
            Margin="0,0,0,22"
            VerticalAlignment="Top"
            Background="Transparent">
            <TextBlock
                VerticalAlignment="Center"
                FontSize="22"
                Style="{StaticResource TitleTextBlockStyle}"
                Text="Browse Repository" />
        </Grid>

        <GridView
            x:Name="VideoGallery"
            ItemTemplate="{StaticResource ThumbnailTemplate}"
            ItemsSource="{x:Bind VideoData}"
            SelectionMode="Single" />
    </StackPanel>
    <Frame x:Name="ContentFrame">
        <Frame.ContentTransitions>
            <TransitionCollection>
                <NavigationThemeTransition />
            </TransitionCollection>
        </Frame.ContentTransitions>
    </Frame>
</Page>

M vorg-windows/BrowsePage.xaml.cpp => vorg-windows/BrowsePage.xaml.cpp +100 -18
@@ 13,38 13,120 @@ namespace winrt::vorg_windows::implementation
BrowsePage::BrowsePage()
{
    InitializeComponent();

    // Initialize app ptr
    mApp.copy_from(get_self<vorg_windows::implementation::App>(
        Application::Current().as<default_interface<vorg_windows::implementation::App>>()));

    // Start in BrowseGalleryPage
    GoToGallery(false);
}

BrowsePage::~BrowsePage()
{

    vorg_windows::BrowseGalleryPage galleryPage = ContentFrame().Content().try_as<vorg_windows::BrowseGalleryPage>();
    if (galleryPage != nullptr)
    {
        galleryPage.ShowVideoDetail(mShowVideoDetailToken);
        return;
    }

    vorg_windows::BrowseDetailPage detailPage = ContentFrame().Content().try_as<vorg_windows::BrowseDetailPage>();
    if (detailPage != nullptr)
    {
        detailPage.BackFromDetail(mBackFromDetailToken);
        detailPage.VideoModified(mVideoModifiedToken);
        return;
    }
}

Windows::Foundation::Collections::IObservableVector<Windows::Foundation::IInspectable> BrowsePage::VideoData() const
Windows::Foundation::Collections::IObservableVector<Windows::Foundation::IInspectable> BrowsePage::GalleryData() const
{
    return mVideoData;
    return mGalleryData;
}

void BrowsePage::VideoData(
void BrowsePage::GalleryData(
    const Windows::Foundation::Collections::IObservableVector<Windows::Foundation::IInspectable> &videoData)
{
    mVideoData = videoData;
    mGalleryData = videoData;

    // Propagate to BrowseGalleryPage
    vorg_windows::BrowseGalleryPage galleryPage = ContentFrame().Content().try_as<vorg_windows::BrowseGalleryPage>();
    if (galleryPage != nullptr)
    {
        galleryPage.GalleryData(videoData);
    }
}

event_token BrowsePage::VideoModified(
    const Windows::Foundation::TypedEventHandler<vorg_windows::BrowsePage, Windows::Foundation::IInspectable> &handler)
{
    return mVideoModifiedEvent.add(handler);
}

void BrowsePage::VideoModified(const event_token &token)
{
    mVideoModifiedEvent.remove(token);
}

void BrowsePage::BrowseGalleryPage_ShowVideoDetail(const vorg_windows::BrowseGalleryPage &, const hstring &args)
{
    // Unsubscribe from ShowVideoDetail
    vorg_windows::BrowseGalleryPage galleryPage = ContentFrame().Content().as<vorg_windows::BrowseGalleryPage>();
    galleryPage.ShowVideoDetail(mShowVideoDetailToken);

    // Get video detail
    vorg_windows::VideoPreviewData previewData{nullptr};
    vorg_windows::VideoDetailData detailData{nullptr};
    mApp->GetVideoDetail(args, detailData, previewData);

    // Navigate to BrowseDetailPage
    Microsoft::UI::Xaml::Media::Animation::SlideNavigationTransitionInfo transitionInfo;
    transitionInfo.Effect(Microsoft::UI::Xaml::Media::Animation::SlideNavigationTransitionEffect::FromRight);
    ContentFrame().Navigate(xaml_typename<vorg_windows::BrowseDetailPage>(), nullptr, transitionInfo);
    vorg_windows::BrowseDetailPage detailPage = ContentFrame().Content().as<vorg_windows::BrowseDetailPage>();
    detailPage.DetailData(detailData);
    detailPage.PreviewData(previewData);
    mVideoModifiedToken =
        detailPage.VideoModified([this](auto &sender, auto &args) { BrowseDetailPage_VideoModified(sender, args); });
    mBackFromDetailToken =
        detailPage.BackFromDetail([this](auto &sender, auto &args) { BrowseDetailPage_BackFromDetail(sender, args); });
}

void BrowsePage::BrowseDetailPage_BackFromDetail(const vorg_windows::BrowseDetailPage &,
                                                 const Windows::Foundation::IInspectable &)
{
    // Unsubscribe from BackFromDetail
    vorg_windows::BrowseDetailPage detailPage = ContentFrame().Content().as<vorg_windows::BrowseDetailPage>();
    detailPage.BackFromDetail(mBackFromDetailToken);

    // Navigate to BrowseGalleryPage
    GoToGallery(true);
}

void BrowsePage::BrowseDetailPage_VideoModified(const vorg_windows::BrowseDetailPage &,
                                                const Windows::Foundation::IInspectable &)
{
    // Propagate event
    mVideoModifiedEvent(*this, nullptr);
}

fire_and_forget BrowsePage::Thumbnail_DoubleTapped(Windows::Foundation::IInspectable const &sender,
                                                   Microsoft::UI::Xaml::Input::DoubleTappedRoutedEventArgs const &)
void BrowsePage::GoToGallery(bool playAnimation)
{
    Microsoft::UI::Xaml::Controls::Image image = sender.as<Microsoft::UI::Xaml::Controls::Image>();
    Windows::Foundation::IInspectable dataContext = image.DataContext();
    vorg_windows::BrowseVideoData videoData = dataContext.as<vorg_windows::BrowseVideoData>();
    Windows::Storage::StorageFile videoFile =
        co_await Windows::Storage::StorageFile::GetFileFromPathAsync(videoData.VideoPath());
    if (videoFile)
    if (playAnimation)
    {
        Windows::System::Launcher::LaunchFileAsync(videoFile);
        Microsoft::UI::Xaml::Media::Animation::SlideNavigationTransitionInfo transitionInfo;
        transitionInfo.Effect(Microsoft::UI::Xaml::Media::Animation::SlideNavigationTransitionEffect::FromLeft);
        ContentFrame().Navigate(xaml_typename<vorg_windows::BrowseGalleryPage>(), nullptr, transitionInfo);
    }
    else
    {
        vorg_windows::ErrorContentDialog dialog;
        dialog.Title(box_value(L"Error opening file"));
        dialog.Content(box_value(L"This should not happen. Please file a bug report."));
        dialog.XamlRoot(XamlRoot());
        co_await dialog.ShowAsync();
        ContentFrame().Navigate(xaml_typename<vorg_windows::BrowseGalleryPage>());
    }
    vorg_windows::BrowseGalleryPage galleryPage = ContentFrame().Content().as<vorg_windows::BrowseGalleryPage>();
    galleryPage.GalleryData(mGalleryData);
    mShowVideoDetailToken = galleryPage.ShowVideoDetail(
        [this](auto &sender, auto &args) { BrowseGalleryPage_ShowVideoDetail(sender, args); });
}
} // namespace winrt::vorg_windows::implementation

M vorg-windows/BrowsePage.xaml.h => vorg-windows/BrowsePage.xaml.h +30 -6
@@ 9,17 9,41 @@ namespace winrt::vorg_windows::implementation
struct BrowsePage : BrowsePageT<BrowsePage>
{
    BrowsePage();
    ~BrowsePage();

    Windows::Foundation::Collections::IObservableVector<Windows::Foundation::IInspectable> VideoData() const;
    void VideoData(
    // Properties
    Windows::Foundation::Collections::IObservableVector<Windows::Foundation::IInspectable> GalleryData() const;
    void GalleryData(
        const Windows::Foundation::Collections::IObservableVector<Windows::Foundation::IInspectable> &videoData);

    // Event
    event_token VideoModified(const Windows::Foundation::TypedEventHandler<vorg_windows::BrowsePage,
                                                                           Windows::Foundation::IInspectable> &handler);
    void VideoModified(const event_token &token);

    // Handlers
    void BrowseGalleryPage_ShowVideoDetail(const vorg_windows::BrowseGalleryPage &, const hstring &args);
    void BrowseDetailPage_BackFromDetail(const vorg_windows::BrowseDetailPage &,
                                         const Windows::Foundation::IInspectable &);
    void BrowseDetailPage_VideoModified(const vorg_windows::BrowseDetailPage &,
                                        const Windows::Foundation::IInspectable &);

  private:
    Windows::Foundation::Collections::IObservableVector<Windows::Foundation::IInspectable> mVideoData;
    // Properties
    Windows::Foundation::Collections::IObservableVector<Windows::Foundation::IInspectable> mGalleryData;

    // Events
    event<Windows::Foundation::TypedEventHandler<vorg_windows::BrowsePage, Windows::Foundation::IInspectable>>
        mVideoModifiedEvent;

    // Members
    com_ptr<vorg_windows::implementation::App> mApp;
    event_token mShowVideoDetailToken;
    event_token mBackFromDetailToken;
    event_token mVideoModifiedToken;

  public:
    fire_and_forget Thumbnail_DoubleTapped(Windows::Foundation::IInspectable const &sender,
                                           Microsoft::UI::Xaml::Input::DoubleTappedRoutedEventArgs const &);
    // Helper functions
    void GoToGallery(bool playAnimation);
};
} // namespace winrt::vorg_windows::implementation


M vorg-windows/ImportEditPage.idl => vorg-windows/ImportEditPage.idl +3 -10
@@ 1,19 1,12 @@
import "ImportEditPageState.idl";
import "VideoPreviewData.idl";

namespace vorg_windows
{
[default_interface] runtimeclass ImportEditPage : Microsoft.UI.Xaml.Controls.Page
{
    ImportEditPage();
    Windows.Foundation.Collections.IObservableVector<String> Actors
    {
        get;
    };
    Windows.Foundation.Collections.IObservableVector<String> Tags
    {
        get;
    };
    ImportEditPageState State

    VideoPreviewData PreviewData
    {
        get;
    };

M vorg-windows/ImportEditPage.xaml => vorg-windows/ImportEditPage.xaml +2 -2
@@ 36,13 36,13 @@
                VerticalAlignment="Center"
                FontSize="22"
                Style="{StaticResource SubtitleTextBlockStyle}"
                Text="{x:Bind Path=State.FileName, Mode=OneWay}" />
                Text="{x:Bind Path=PreviewData.FileName, Mode=OneWay}" />
            <MediaPlayerElement
                x:Name="MediaPlayer"
                AreTransportControlsEnabled="True"
                BorderThickness="0"
                CornerRadius="16"
                Source="{x:Bind Path=State.MediaSource, Mode=OneWay}" />
                Source="{x:Bind Path=PreviewData.MediaSource, Mode=OneWay}" />

            <local:VideoDataEditor
                x:Name="Editor"

M vorg-windows/ImportEditPage.xaml.cpp => vorg-windows/ImportEditPage.xaml.cpp +5 -15
@@ 11,7 11,7 @@ using namespace Microsoft::UI::Xaml;
namespace winrt::vorg_windows::implementation
{
ImportEditPage::ImportEditPage()
    : mActors(single_threaded_observable_vector<hstring>()), mTags(single_threaded_observable_vector<hstring>())

{
    InitializeComponent();



@@ 20,19 20,9 @@ ImportEditPage::ImportEditPage()
        Application::Current().as<default_interface<vorg_windows::implementation::App>>()));
}

Windows::Foundation::Collections::IObservableVector<hstring> ImportEditPage::Actors() const
{
    return mActors;
}

Windows::Foundation::Collections::IObservableVector<hstring> ImportEditPage::Tags() const
{
    return mTags;
}

vorg_windows::ImportEditPageState ImportEditPage::State() const
vorg_windows::VideoPreviewData ImportEditPage::PreviewData() const
{
    return mState;
    return mPreviewData;
}

void ImportEditPage::SubmitButton_Click(const Windows::Foundation::IInspectable &,


@@ 42,8 32,8 @@ void ImportEditPage::SubmitButton_Click(const Windows::Foundation::IInspectable 
    Editor().AddAllCurrent();

    // Import the file
    mApp->ImportNext(Editor().State().Title(), Editor().State().Studio(), Editor().State().Actors(),
                     Editor().State().Tags());
    mApp->ImportNext(Editor().DetailData().Title(), Editor().DetailData().Studio(), Editor().DetailData().Actors(),
                     Editor().DetailData().Tags());
    Editor().ClearState();
    mEditFinishedEvent(*this, nullptr);
}

M vorg-windows/ImportEditPage.xaml.h => vorg-windows/ImportEditPage.xaml.h +2 -7
@@ 10,10 10,7 @@ struct ImportEditPage : ImportEditPageT<ImportEditPage>
{
    ImportEditPage();

    Windows::Foundation::Collections::IObservableVector<hstring> Actors() const;
    Windows::Foundation::Collections::IObservableVector<hstring> Tags() const;

    vorg_windows::ImportEditPageState State() const;
    vorg_windows::VideoPreviewData PreviewData() const;

    void SubmitButton_Click(const Windows::Foundation::IInspectable &, const Microsoft::UI::Xaml::RoutedEventArgs &);



@@ 33,9 30,7 @@ struct ImportEditPage : ImportEditPageT<ImportEditPage>
    void EditFinished(const event_token &token);

  private:
    Windows::Foundation::Collections::IObservableVector<hstring> mActors;
    Windows::Foundation::Collections::IObservableVector<hstring> mTags;
    vorg_windows::ImportEditPageState mState;
    vorg_windows::VideoPreviewData mPreviewData;
    com_ptr<vorg_windows::implementation::App> mApp;
    event<Windows::Foundation::TypedEventHandler<vorg_windows::ImportEditPage, Windows::Foundation::IInspectable>>
        mEditFinishedEvent;

M vorg-windows/ImportPage.xaml.cpp => vorg-windows/ImportPage.xaml.cpp +4 -8
@@ 45,10 45,8 @@ void ImportPage::ImportSelectionPage_SelectionFinished(const vorg_windows::Impor
    vorg_windows::ImportEditPage editPage = ContentFrame().Content().as<vorg_windows::ImportEditPage>();
    hstring filePath = mApp->GetNextFile();
    hstring fileName = mApp->GetNextFileName();
    editPage.State().FileName(fileName);
    Windows::Media::Core::MediaSource mediaSouce =
        Windows::Media::Core::MediaSource::CreateFromUri(Windows::Foundation::Uri{filePath});
    editPage.State().MediaSource(mediaSouce);
    editPage.PreviewData().FileName(fileName);
    editPage.PreviewData().VideoPath(filePath);
    mEditFinishedToken =
        editPage.EditFinished([this](auto &sender, auto &args) { this->ImportEditPage_EditFinished(sender, args); });
}


@@ 83,10 81,8 @@ void ImportPage::ImportEditPage_EditFinished(const vorg_windows::ImportEditPage 
    {
        // Continue editing next file
        hstring fileName = mApp->GetNextFileName();
        editPage.State().FileName(fileName);
        Windows::Media::Core::MediaSource mediaSouce =
            Windows::Media::Core::MediaSource::CreateFromUri(Windows::Foundation::Uri{filePath});
        editPage.State().MediaSource(mediaSouce);
        editPage.PreviewData().FileName(fileName);
        editPage.PreviewData().VideoPath(filePath);
    }
}


M vorg-windows/MainWindow.xaml.cpp => vorg-windows/MainWindow.xaml.cpp +56 -10
@@ 16,7 16,7 @@ using namespace Microsoft::UI::Xaml;
namespace winrt::vorg_windows::implementation
{

MainWindow::MainWindow() : mVideoData(single_threaded_observable_vector<Windows::Foundation::IInspectable>())
MainWindow::MainWindow() : mGalleryData(single_threaded_observable_vector<Windows::Foundation::IInspectable>())
{
    InitializeComponent();



@@ 36,12 36,15 @@ MainWindow::MainWindow() : mVideoData(single_threaded_observable_vector<Windows:
    mExtraSetupMap.emplace(L"Import", [this]() {
        // Handle VideoImported
        vorg_windows::ImportPage importPage = ContentFrame().Content().as<vorg_windows::ImportPage>();
        importPage.VideoImported([this](auto &sender, auto &args) { ImportPage_VideoImported(sender, args); });
        mVideoImportedEventToken =
            importPage.VideoImported([this](auto &sender, auto &args) { ImportPage_VideoImported(sender, args); });
    });
    mExtraSetupMap.emplace(L"Browse", [this]() {
        // Set videoData
        // Set GalleryData and handle VideoModified
        vorg_windows::BrowsePage browsePage = ContentFrame().Content().as<vorg_windows::BrowsePage>();
        browsePage.VideoData(mVideoData);
        browsePage.GalleryData(mGalleryData);
        mVideoModifiedEventToken =
            browsePage.VideoModified([this](auto &sender, auto &args) { BrowsePage_VideoModified(sender, args); });
    });

    // Navigate to LandingPage


@@ 57,11 60,19 @@ void MainWindow::NavigationView_ItemInvoked(const Controls::NavigationView &,
                                            const Controls::NavigationViewItemInvokedEventArgs &args)
{
    const hstring &tag = args.InvokedItemContainer().Tag().as<hstring>();
    ContentFrame().Navigate(mNavigationTypeMap.at(tag));
    auto setupFuncIt = mExtraSetupMap.find(tag);
    if (setupFuncIt != mExtraSetupMap.end())
    Windows::UI::Xaml::Interop::TypeName targetType = mNavigationTypeMap.at(tag);

    if (ContentFrame().CurrentSourcePageType() != targetType)
    {
        setupFuncIt->second();
        // Cleanup
        PageCleanup();

        ContentFrame().Navigate(targetType);
        auto setupFuncIt = mExtraSetupMap.find(tag);
        if (setupFuncIt != mExtraSetupMap.end())
        {
            setupFuncIt->second();
        }
    }
}



@@ 70,9 81,10 @@ void MainWindow::LandingPage_OpenRepo(const vorg_windows::LandingPage &, const W
    // Remove OpenRepo handler
    vorg_windows::LandingPage landingPage = ContentFrame().Content().as<vorg_windows::LandingPage>();
    landingPage.OpenRepo(mOpenRepoEventToken);
    mOpenRepoEventToken = event_token();

    // Populate video data
    mApp->PopulateVideoData(mVideoData);
    mApp->PopulateGalleryData(mGalleryData);

    // Navigate to BrowsePage
    ContentFrame().Navigate(mNavigationTypeMap.at(L"Browse"));


@@ 90,6 102,40 @@ void MainWindow::LandingPage_OpenRepo(const vorg_windows::LandingPage &, const W

void MainWindow::ImportPage_VideoImported(const vorg_windows::ImportPage &, const Windows::Foundation::IInspectable &)
{
    mApp->PopulateVideoData(mVideoData);
    mApp->PopulateGalleryData(mGalleryData);
}

void MainWindow::BrowsePage_VideoModified(const vorg_windows::BrowsePage &, const Windows::Foundation::IInspectable &)
{
    mApp->PopulateGalleryData(mGalleryData);
}

void MainWindow::PageCleanup()
{
    {
        vorg_windows::BrowsePage browsePage = ContentFrame().Content().try_as<vorg_windows::BrowsePage>();
        if (browsePage != nullptr)
        {
            browsePage.VideoModified(mVideoModifiedEventToken);
            mVideoModifiedEventToken = event_token();
            return;
        }
    }
    {
        vorg_windows::ImportPage importPage = ContentFrame().Content().try_as<vorg_windows::ImportPage>();
        if (importPage != nullptr)
        {
            importPage.VideoImported(mVideoImportedEventToken);
            mVideoImportedEventToken = event_token();
            return;
        }
    }
    {
        vorg_windows::ManagePage managePage = ContentFrame().Content().try_as<vorg_windows::ManagePage>();
        if (managePage != nullptr)
        {
            return;
        }
    }
}
} // namespace winrt::vorg_windows::implementation

M vorg-windows/MainWindow.xaml.h => vorg-windows/MainWindow.xaml.h +9 -1
@@ 13,6 13,7 @@ struct MainWindow : MainWindowT<MainWindow>
{
    MainWindow();

    // Handlers
    void NavigationView_ItemInvoked(const Microsoft::UI::Xaml::Controls::NavigationView &sender,
                                    const Microsoft::UI::Xaml::Controls::NavigationViewItemInvokedEventArgs &args);



@@ 20,15 21,22 @@ struct MainWindow : MainWindowT<MainWindow>

    void ImportPage_VideoImported(const vorg_windows::ImportPage &, const Windows::Foundation::IInspectable &);

    void BrowsePage_VideoModified(const vorg_windows::BrowsePage &, const Windows::Foundation::IInspectable &);

  private:
    // Members
    event_token mOpenRepoEventToken;
    event_token mVideoImportedEventToken;
    event_token mVideoModifiedEventToken;
    std::map<hstring, Windows::UI::Xaml::Interop::TypeName> mNavigationTypeMap;
    std::map<hstring, std::function<void()>> mExtraSetupMap;
    Windows::Foundation::Collections::IObservableVector<Windows::Foundation::IInspectable> mVideoData;
    Windows::Foundation::Collections::IObservableVector<Windows::Foundation::IInspectable> mGalleryData;

    // App ptr
    com_ptr<vorg_windows::implementation::App> mApp{nullptr};

    // Helper functions
    void PageCleanup();
};
} // namespace winrt::vorg_windows::implementation


M vorg-windows/VideoDataEditor.idl => vorg-windows/VideoDataEditor.idl +3 -2
@@ 1,4 1,4 @@
import "VideoDataEditorState.idl";
import "VideoDetailData.idl";

namespace vorg_windows
{


@@ 6,9 6,10 @@ namespace vorg_windows
{
    VideoDataEditor();

    VideoDataEditorState State
    VideoDetailData DetailData
    {
        get;
        set;
    };

    event Windows.Foundation.TypedEventHandler<Microsoft.UI.Xaml.Controls.AutoSuggestBox,

M vorg-windows/VideoDataEditor.xaml => vorg-windows/VideoDataEditor.xaml +6 -6
@@ 28,7 28,7 @@
            x:Name="TitleInput"
            Grid.Row="1"
            Grid.Column="0"
            Text="{x:Bind Path=State.Title, Mode=TwoWay}" />
            Text="{x:Bind Path=DetailData.Title, Mode=TwoWay}" />

        <!--  Studio  -->
        <TextBlock Grid.Row="0" Grid.Column="1">Studio</TextBlock>


@@ 36,7 36,7 @@
            x:Name="StudioInput"
            Grid.Row="1"
            Grid.Column="1"
            Text="{x:Bind Path=State.Studio, Mode=TwoWay}" />
            Text="{x:Bind Path=DetailData.Studio, Mode=TwoWay}" />
    </Grid>

    <Grid


@@ 80,7 80,7 @@
        <ListView
            Grid.Row="2"
            Grid.Column="0"
            ItemsSource="{x:Bind Path=State.Actors, Mode=TwoWay}"
            ItemsSource="{x:Bind Path=DetailData.Actors, Mode=TwoWay}"
            SelectionMode="Single">
            <ItemsControl.ItemTemplate>
                <DataTemplate x:DataType="x:String">


@@ 102,7 102,7 @@
                                FontFamily="Segoe Fluent Icons"
                                Visibility="Collapsed" />
                            <VisualStateManager.VisualStateGroups>
                                <VisualStateGroup x:Name="CommonStates">
                                <VisualStateGroup>
                                    <VisualState x:Name="HoverButtonsHidden" />
                                    <VisualState x:Name="HoverButtonsShown">
                                        <VisualState.Setters>


@@ 149,7 149,7 @@
        <ListView
            Grid.Row="2"
            Grid.Column="1"
            ItemsSource="{x:Bind Path=State.Tags, Mode=TwoWay}"
            ItemsSource="{x:Bind Path=DetailData.Tags, Mode=TwoWay}"
            SelectionMode="Single">
            <ItemsControl.ItemTemplate>
                <DataTemplate x:DataType="x:String">


@@ 171,7 171,7 @@
                                FontFamily="Segoe Fluent Icons"
                                Visibility="Collapsed" />
                            <VisualStateManager.VisualStateGroups>
                                <VisualStateGroup x:Name="CommonStates">
                                <VisualStateGroup>
                                    <VisualState x:Name="HoverButtonsHidden" />
                                    <VisualState x:Name="HoverButtonsShown">
                                        <VisualState.Setters>

M vorg-windows/VideoDataEditor.xaml.cpp => vorg-windows/VideoDataEditor.xaml.cpp +17 -12
@@ 15,9 15,14 @@ VideoDataEditor::VideoDataEditor()
    InitializeComponent();
}

vorg_windows::VideoDataEditorState VideoDataEditor::State() const
vorg_windows::VideoDetailData VideoDataEditor::DetailData() const
{
    return mState;
    return mDetailData;
}

void VideoDataEditor::DetailData(const vorg_windows::VideoDetailData &detailData)
{
    mDetailData = detailData;
}

void VideoDataEditor::ActorNameInput_KeyUp(Windows::Foundation::IInspectable const &,


@@ 44,7 49,7 @@ void VideoDataEditor::ActorsDeleteButton_Click(const Windows::Foundation::IInspe
    Microsoft::UI::Xaml::Controls::Button button = sender.as<Microsoft::UI::Xaml::Controls::Button>();
    Windows::Foundation::IInspectable dataContext = button.DataContext();
    hstring itemName = dataContext.as<hstring>();
    DeleteFromVector(mState.Actors(), itemName);
    DeleteFromVector(mDetailData.Actors(), itemName);
}

void VideoDataEditor::TagsDeleteButton_Click(const Windows::Foundation::IInspectable &sender,


@@ 53,7 58,7 @@ void VideoDataEditor::TagsDeleteButton_Click(const Windows::Foundation::IInspect
    Microsoft::UI::Xaml::Controls::Button button = sender.as<Microsoft::UI::Xaml::Controls::Button>();
    Windows::Foundation::IInspectable dataContext = button.DataContext();
    hstring itemName = dataContext.as<hstring>();
    DeleteFromVector(mState.Tags(), itemName);
    DeleteFromVector(mDetailData.Tags(), itemName);
}

void VideoDataEditor::ActorsSubmitButton_Click(const Windows::Foundation::IInspectable &,


@@ 69,14 74,14 @@ void VideoDataEditor::TagsSubmitButton_Click(const Windows::Foundation::IInspect
}

void VideoDataEditor::UserControl_PointerEntered(const Windows::Foundation::IInspectable &sender,
                                                const Microsoft::UI::Xaml::Input::PointerRoutedEventArgs &)
                                                 const Microsoft::UI::Xaml::Input::PointerRoutedEventArgs &)
{
    Microsoft::UI::Xaml::Controls::UserControl userControl = sender.as<Microsoft::UI::Xaml::Controls::UserControl>();
    VisualStateManager::GoToState(userControl, L"HoverButtonsShown", false);
}

void VideoDataEditor::UserControl_PointerExited(const Windows::Foundation::IInspectable &sender,
                                               const Microsoft::UI::Xaml::Input::PointerRoutedEventArgs &)
                                                const Microsoft::UI::Xaml::Input::PointerRoutedEventArgs &)
{
    Microsoft::UI::Xaml::Controls::UserControl userControl = sender.as<Microsoft::UI::Xaml::Controls::UserControl>();
    VisualStateManager::GoToState(userControl, L"HoverButtonsHidden", false);


@@ 94,8 99,8 @@ void VideoDataEditor::ClearState()
    StudioInput().Text(L"");
    ActorNameInput().Text(L"");
    TagNameInput().Text(L"");
    mState.Actors().Clear();
    mState.Tags().Clear();
    mDetailData.Actors().Clear();
    mDetailData.Tags().Clear();
}

event_token VideoDataEditor::StudioTextChanged(


@@ 142,9 147,9 @@ void VideoDataEditor::AddToActors(const hstring &actor)
    std::wstring_view nameView = actor;
    TrimStringView(nameView);
    hstring trimmedName{nameView};
    if (!trimmedName.empty() && !FindInVector(mState.Actors(), trimmedName))
    if (!trimmedName.empty() && !FindInVector(mDetailData.Actors(), trimmedName))
    {
        mState.Actors().Append(trimmedName);
        mDetailData.Actors().Append(trimmedName);
    }
    ActorNameInput().Text(L"");
}


@@ 154,9 159,9 @@ void VideoDataEditor::AddToTags(const hstring &tag)
    std::wstring_view nameView = tag;
    TrimStringView(nameView);
    hstring trimmedName{nameView};
    if (!nameView.empty() && !FindInVector(mState.Actors(), trimmedName))
    if (!nameView.empty() && !FindInVector(mDetailData.Tags(), trimmedName))
    {
        mState.Tags().Append(trimmedName);
        mDetailData.Tags().Append(trimmedName);
    }
    TagNameInput().Text(L"");
}

M vorg-windows/VideoDataEditor.xaml.h => vorg-windows/VideoDataEditor.xaml.h +13 -8
@@ 5,7 5,7 @@

#include "VideoDataEditor.g.h"

#include "VideoDataEditorState.h"
#include "VideoDetailData.h"

namespace winrt::vorg_windows::implementation
{


@@ 13,8 13,11 @@ struct VideoDataEditor : VideoDataEditorT<VideoDataEditor>
{
    VideoDataEditor();

    vorg_windows::VideoDataEditorState State() const;
    // Properties
    vorg_windows::VideoDetailData DetailData() const;
    void DetailData(const vorg_windows::VideoDetailData &detailData);

    // Handlers
    void ActorNameInput_KeyUp(Windows::Foundation::IInspectable const &,
                              Microsoft::UI::Xaml::Input::KeyRoutedEventArgs const &e);
    void TagNameInput_KeyUp(Windows::Foundation::IInspectable const &,


@@ 35,10 38,7 @@ struct VideoDataEditor : VideoDataEditorT<VideoDataEditor>
    void UserControl_PointerExited(const Windows::Foundation::IInspectable &sender,
                                   const Microsoft::UI::Xaml::Input::PointerRoutedEventArgs &);

    void AddAllCurrent();

    void ClearState();

    // Events
    event_token StudioTextChanged(
        const Windows::Foundation::TypedEventHandler<Microsoft::UI::Xaml::Controls::AutoSuggestBox,
                                                     Microsoft::UI::Xaml::Controls::AutoSuggestBoxTextChangedEventArgs>


@@ 57,9 57,14 @@ struct VideoDataEditor : VideoDataEditorT<VideoDataEditor>
            &handler);
    void TagTextChanged(const event_token &token);

    // Methods
    void AddAllCurrent();

    void ClearState();

  private:
    // Members
    vorg_windows::VideoDataEditorState mState;
    // Properties
    vorg_windows::VideoDetailData mDetailData;

    // Helper functions
    void AddToActors(const hstring &actor);

R vorg-windows/VideoDataEditorState.cpp => vorg-windows/VideoDetailData.cpp +24 -14
@@ 1,67 1,77 @@
#include "pch.h"

#include "VideoDataEditorState.h"
#if __has_include("VideoDataEditorState.g.cpp")
#include "VideoDataEditorState.g.cpp"
#include "VideoDetailData.h"
#if __has_include("VideoDetailData.g.cpp")
#include "VideoDetailData.g.cpp"
#endif

namespace winrt::vorg_windows::implementation
{
VideoDataEditorState::VideoDataEditorState()
VideoDetailData::VideoDetailData()
    : mActors(single_threaded_observable_vector<hstring>()), mTags(single_threaded_observable_vector<hstring>())
{
}

hstring VideoDataEditorState::Title() const
hstring VideoDetailData::Hash() const
{
    return mHash;
}

void VideoDetailData::Hash(const hstring &hash)
{
    mHash = hash;
}

hstring VideoDetailData::Title() const
{
    return mTitle;
}

void VideoDataEditorState::Title(const hstring &title)
void VideoDetailData::Title(const hstring &title)
{
    mTitle = title;
    mPropertyChangedEvent(*this, Microsoft::UI::Xaml::Data::PropertyChangedEventArgs{L"Title"});
}

hstring VideoDataEditorState::Studio() const
hstring VideoDetailData::Studio() const
{
    return mStudio;
}

void VideoDataEditorState::Studio(const hstring &studio)
void VideoDetailData::Studio(const hstring &studio)
{
    mStudio = studio;
    mPropertyChangedEvent(*this, Microsoft::UI::Xaml::Data::PropertyChangedEventArgs{L"Studio"});
}

Windows::Foundation::Collections::IObservableVector<hstring> VideoDataEditorState::Actors() const
Windows::Foundation::Collections::IObservableVector<hstring> VideoDetailData::Actors() const
{
    return mActors;
}

void VideoDataEditorState::Actors(const Windows::Foundation::Collections::IObservableVector<hstring> &actors)
void VideoDetailData::Actors(const Windows::Foundation::Collections::IObservableVector<hstring> &actors)
{
    mActors = actors;
    mPropertyChangedEvent(*this, Microsoft::UI::Xaml::Data::PropertyChangedEventArgs{L"Actors"});
}

Windows::Foundation::Collections::IObservableVector<hstring> VideoDataEditorState::Tags() const
Windows::Foundation::Collections::IObservableVector<hstring> VideoDetailData::Tags() const
{
    return mTags;
}

void VideoDataEditorState::Tags(const Windows::Foundation::Collections::IObservableVector<hstring> &tags)
void VideoDetailData::Tags(const Windows::Foundation::Collections::IObservableVector<hstring> &tags)
{
    mTags = tags;
    mPropertyChangedEvent(*this, Microsoft::UI::Xaml::Data::PropertyChangedEventArgs{L"Tags"});
}

event_token VideoDataEditorState::PropertyChanged(const Microsoft::UI::Xaml::Data::PropertyChangedEventHandler &handler)
event_token VideoDetailData::PropertyChanged(const Microsoft::UI::Xaml::Data::PropertyChangedEventHandler &handler)
{
    return mPropertyChangedEvent.add(handler);
}

void VideoDataEditorState::PropertyChanged(const event_token &token)
void VideoDetailData::PropertyChanged(const event_token &token)
{
    mPropertyChangedEvent.remove(token);
}

R vorg-windows/VideoDataEditorState.h => vorg-windows/VideoDetailData.h +8 -4
@@ 1,12 1,15 @@
#pragma once

#include "VideoDataEditorState.g.h"
#include "VideoDetailData.g.h"

namespace winrt::vorg_windows::implementation
{
struct VideoDataEditorState : VideoDataEditorStateT<VideoDataEditorState>
struct VideoDetailData : VideoDetailDataT<VideoDetailData>
{
    VideoDataEditorState();
    VideoDetailData();

    hstring Hash() const;
    void Hash(const hstring &hash);

    hstring Title() const;
    void Title(const hstring &title);


@@ 24,6 27,7 @@ struct VideoDataEditorState : VideoDataEditorStateT<VideoDataEditorState>
    void PropertyChanged(const event_token &token);

  private:
    hstring mHash;
    hstring mTitle;
    hstring mStudio;



@@ 36,7 40,7 @@ struct VideoDataEditorState : VideoDataEditorStateT<VideoDataEditorState>

namespace winrt::vorg_windows::factory_implementation
{
struct VideoDataEditorState : VideoDataEditorStateT<VideoDataEditorState, implementation::VideoDataEditorState>
struct VideoDetailData : VideoDetailDataT<VideoDetailData, implementation::VideoDetailData>
{
};
} // namespace winrt::vorg_windows::factory_implementation

R vorg-windows/VideoDataEditorState.idl => vorg-windows/VideoDetailData.idl +7 -2
@@ 1,9 1,14 @@
namespace vorg_windows
{
[default_interface] runtimeclass VideoDataEditorState : Microsoft.UI.Xaml.Data.INotifyPropertyChanged
[default_interface] runtimeclass VideoDetailData : Microsoft.UI.Xaml.Data.INotifyPropertyChanged
{
    VideoDataEditorState();
    VideoDetailData();

    String Hash
    {
        get;
        set;
    };
    String Title
    {
        get;

R vorg-windows/BrowseVideoData.cpp => vorg-windows/VideoGalleryData.cpp +17 -7
@@ 1,28 1,38 @@
#include "pch.h"

#include "BrowseVideoData.h"
#if __has_include("BrowseVideoData.g.cpp")
#include "BrowseVideoData.g.cpp"
#include "VideoGalleryData.h"
#if __has_include("VideoGalleryData.g.cpp")
#include "VideoGalleryData.g.cpp"
#endif

namespace winrt::vorg_windows::implementation
{
hstring BrowseVideoData::ThumbnailPath() const
hstring VideoGalleryData::Hash() const
{
    return mHash;
}

void VideoGalleryData::Hash(const hstring &hash)
{
    mHash = hash;
}

hstring VideoGalleryData::ThumbnailPath() const
{
    return mThumbnailPath;
}

void BrowseVideoData::ThumbnailPath(const hstring &thumbnailPath)
void VideoGalleryData::ThumbnailPath(const hstring &thumbnailPath)
{
    mThumbnailPath = thumbnailPath;
}

hstring BrowseVideoData::VideoPath() const
hstring VideoGalleryData::VideoPath() const
{
    return mVideoPath;
}

void BrowseVideoData::VideoPath(const hstring &videoPath)
void VideoGalleryData::VideoPath(const hstring &videoPath)
{
    mVideoPath = videoPath;
}

R vorg-windows/BrowseVideoData.h => vorg-windows/VideoGalleryData.h +7 -3
@@ 1,11 1,14 @@
#pragma once

#include "BrowseVideoData.g.h"
#include "VideoGalleryData.g.h"

namespace winrt::vorg_windows::implementation
{
struct BrowseVideoData : BrowseVideoDataT<BrowseVideoData>
struct VideoGalleryData : VideoGalleryDataT<VideoGalleryData>
{
    hstring Hash() const;
    void Hash(const hstring &hash);

    hstring ThumbnailPath() const;
    void ThumbnailPath(const hstring &thumbnailPath);



@@ 13,6 16,7 @@ struct BrowseVideoData : BrowseVideoDataT<BrowseVideoData>
    void VideoPath(const hstring &videoPath);

  private:
    hstring mHash;
    hstring mThumbnailPath;
    hstring mVideoPath;
};


@@ 20,7 24,7 @@ struct BrowseVideoData : BrowseVideoDataT<BrowseVideoData>

namespace winrt::vorg_windows::factory_implementation
{
struct BrowseVideoData : BrowseVideoDataT<BrowseVideoData, implementation::BrowseVideoData>
struct VideoGalleryData : VideoGalleryDataT<VideoGalleryData, implementation::VideoGalleryData>
{
};
} // namespace winrt::vorg_windows::factory_implementation

R vorg-windows/BrowseVideoData.idl => vorg-windows/VideoGalleryData.idl +3 -2
@@ 1,8 1,9 @@
namespace vorg_windows
{
[default_interface] runtimeclass BrowseVideoData {
    BrowseVideoData();
[default_interface] runtimeclass VideoGalleryData {
    VideoGalleryData();

    String Hash;
    String ThumbnailPath;
    String VideoPath;
}

R vorg-windows/ImportEditPageState.cpp => vorg-windows/VideoPreviewData.cpp +17 -12
@@ 1,40 1,45 @@
#include "pch.h"

#include "ImportEditPageState.h"
#if __has_include("ImportEditPageState.g.cpp")
#include "ImportEditPageState.g.cpp"
#include "VideoPreviewData.h"
#if __has_include("VideoPreviewData.g.cpp")
#include "VideoPreviewData.g.cpp"
#endif

namespace winrt::vorg_windows::implementation
{
Windows::Media::Core::MediaSource ImportEditPageState::MediaSource() const
hstring VideoPreviewData::VideoPath() const
{
    return mMediaSource;
    return mVideoPath;
}

void ImportEditPageState::MediaSource(const Windows::Media::Core::MediaSource &mediaSource)
void VideoPreviewData::VideoPath(const hstring &videoPath)
{
    mMediaSource = mediaSource;
    mPropertyChangedEvent(*this, Microsoft::UI::Xaml::Data::PropertyChangedEventArgs{L"MediaSource"});
    mVideoPath = videoPath;
    mPropertyChangedEvent(*this, Microsoft::UI::Xaml::Data::PropertyChangedEventArgs{L"VideoPath"});
}

hstring ImportEditPageState::FileName() const
hstring VideoPreviewData::FileName() const
{
    return mFileName;
}

void ImportEditPageState::FileName(const hstring &fileName)
void VideoPreviewData::FileName(const hstring &fileName)
{
    mFileName = fileName;
    mPropertyChangedEvent(*this, Microsoft::UI::Xaml::Data::PropertyChangedEventArgs{L"FileName"});
}

event_token ImportEditPageState::PropertyChanged(const Microsoft::UI::Xaml::Data::PropertyChangedEventHandler &handler)
Windows::Media::Core::MediaSource VideoPreviewData::MediaSource()
{
    return Windows::Media::Core::MediaSource::CreateFromUri(Windows::Foundation::Uri{mVideoPath});
}

event_token VideoPreviewData::PropertyChanged(const Microsoft::UI::Xaml::Data::PropertyChangedEventHandler &handler)
{
    return mPropertyChangedEvent.add(handler);
}

void ImportEditPageState::PropertyChanged(const event_token &token)
void VideoPreviewData::PropertyChanged(const event_token &token)
{
    mPropertyChangedEvent.remove(token);
}

R vorg-windows/ImportEditPageState.h => vorg-windows/VideoPreviewData.h +9 -7
@@ 1,24 1,26 @@
#pragma once

#include "ImportEditPageState.g.h"
#include "VideoPreviewData.g.h"

namespace winrt::vorg_windows::implementation
{
struct ImportEditPageState : ImportEditPageStateT<ImportEditPageState>
struct VideoPreviewData : VideoPreviewDataT<VideoPreviewData>
{
    ImportEditPageState() = default;
    VideoPreviewData() = default;

    Windows::Media::Core::MediaSource MediaSource() const;
    void MediaSource(const Windows::Media::Core::MediaSource &mediaSource);
    hstring VideoPath() const;
    void VideoPath(const hstring &videoPath);

    hstring FileName() const;
    void FileName(const hstring &fileName);

    Windows::Media::Core::MediaSource MediaSource();

    event_token PropertyChanged(const Microsoft::UI::Xaml::Data::PropertyChangedEventHandler &handler);
    void PropertyChanged(const event_token &token);

  private:
    Windows::Media::Core::MediaSource mMediaSource{nullptr};
    hstring mVideoPath;
    hstring mFileName;

    event<Microsoft::UI::Xaml::Data::PropertyChangedEventHandler> mPropertyChangedEvent;


@@ 27,7 29,7 @@ struct ImportEditPageState : ImportEditPageStateT<ImportEditPageState>

namespace winrt::vorg_windows::factory_implementation
{
struct ImportEditPageState : ImportEditPageStateT<ImportEditPageState, implementation::ImportEditPageState>
struct VideoPreviewData : VideoPreviewDataT<VideoPreviewData, implementation::VideoPreviewData>
{
};
} // namespace winrt::vorg_windows::factory_implementation

R vorg-windows/ImportEditPageState.idl => vorg-windows/VideoPreviewData.idl +7 -3
@@ 1,14 1,18 @@
namespace vorg_windows
{
[default_interface] runtimeclass ImportEditPageState : Microsoft.UI.Xaml.Data.INotifyPropertyChanged
[default_interface] runtimeclass VideoPreviewData : Microsoft.UI.Xaml.Data.INotifyPropertyChanged
{
    ImportEditPageState();
    VideoPreviewData();

    Windows.Media.Core.MediaSource MediaSource
    String VideoPath
    {
        get;
        set;
    };
    Windows.Media.Core.MediaSource MediaSource
    {
        get;
    };
    String FileName
    {
        get;

M vorg-windows/pch.h => vorg-windows/pch.h +1 -0
@@ 6,6 6,7 @@
#include <restrictederrorinfo.h>
#include <unknwn.h>
#include <windows.h>
#include <winstring.h>

// Undefine GetCurrentTime macro to prevent
// conflict with Storyboard::GetCurrentTime

M vorg-windows/vorg-windows.vcxproj => vorg-windows/vorg-windows.vcxproj +9 -9
@@ 135,7 135,7 @@
      <DependentUpon>BrowsePage.xaml</DependentUpon>
      <SubType>Code</SubType>
    </ClInclude>
    <ClInclude Include="BrowseVideoData.h" />
    <ClInclude Include="VideoGalleryData.h" />
    <ClInclude Include="ErrorContentDialog.xaml.h">
      <DependentUpon>ErrorContentDialog.xaml</DependentUpon>
      <SubType>Code</SubType>


@@ 144,7 144,7 @@
      <DependentUpon>ImportEditPage.xaml</DependentUpon>
      <SubType>Code</SubType>
    </ClInclude>
    <ClInclude Include="ImportEditPageState.h" />
    <ClInclude Include="VideoPreviewData.h" />
    <ClInclude Include="ImportPage.xaml.h">
      <DependentUpon>ImportPage.xaml</DependentUpon>
      <SubType>Code</SubType>


@@ 168,7 168,7 @@
    <ClInclude Include="MainWindow.xaml.h">
      <DependentUpon>MainWindow.xaml</DependentUpon>
    </ClInclude>
    <ClInclude Include="VideoDataEditorState.h" />
    <ClInclude Include="VideoDetailData.h" />
    <ClInclude Include="VideoDataEditor.xaml.h">
      <DependentUpon>VideoDataEditor.xaml</DependentUpon>
      <SubType>Code</SubType>


@@ 221,7 221,7 @@
      <DependentUpon>BrowsePage.xaml</DependentUpon>
      <SubType>Code</SubType>
    </ClCompile>
    <ClCompile Include="BrowseVideoData.cpp" />
    <ClCompile Include="VideoGalleryData.cpp" />
    <ClCompile Include="ErrorContentDialog.xaml.cpp">
      <DependentUpon>ErrorContentDialog.xaml</DependentUpon>
      <SubType>Code</SubType>


@@ 230,7 230,7 @@
      <DependentUpon>ImportEditPage.xaml</DependentUpon>
      <SubType>Code</SubType>
    </ClCompile>
    <ClCompile Include="ImportEditPageState.cpp" />
    <ClCompile Include="VideoPreviewData.cpp" />
    <ClCompile Include="ImportPage.xaml.cpp">
      <DependentUpon>ImportPage.xaml</DependentUpon>
      <SubType>Code</SubType>


@@ 257,7 257,7 @@
      <DependentUpon>MainWindow.xaml</DependentUpon>
    </ClCompile>
    <ClCompile Include="$(GeneratedFilesDir)module.g.cpp" />
    <ClCompile Include="VideoDataEditorState.cpp" />
    <ClCompile Include="VideoDetailData.cpp" />
    <ClCompile Include="VideoDataEditor.xaml.cpp">
      <DependentUpon>VideoDataEditor.xaml</DependentUpon>
      <SubType>Code</SubType>


@@ 280,7 280,7 @@
      <DependentUpon>BrowsePage.xaml</DependentUpon>
      <SubType>Code</SubType>
    </Midl>
    <Midl Include="BrowseVideoData.idl" />
    <Midl Include="VideoGalleryData.idl" />
    <Midl Include="ErrorContentDialog.idl">
      <DependentUpon>ErrorContentDialog.xaml</DependentUpon>
      <SubType>Code</SubType>


@@ 289,7 289,7 @@
      <DependentUpon>ImportEditPage.xaml</DependentUpon>
      <SubType>Code</SubType>
    </Midl>
    <Midl Include="ImportEditPageState.idl" />
    <Midl Include="VideoPreviewData.idl" />
    <Midl Include="ImportPage.idl">
      <DependentUpon>ImportPage.xaml</DependentUpon>
      <SubType>Code</SubType>


@@ 310,7 310,7 @@
      <DependentUpon>ManagePage.xaml</DependentUpon>
      <SubType>Code</SubType>
    </Midl>
    <Midl Include="VideoDataEditorState.idl" />
    <Midl Include="VideoDetailData.idl" />
    <Midl Include="VideoDataEditor.idl">
      <DependentUpon>VideoDataEditor.xaml</DependentUpon>
      <SubType>Code</SubType>

M vorg-windows/vorg-windows.vcxproj.filters => vorg-windows/vorg-windows.vcxproj.filters +9 -9
@@ 31,38 31,38 @@
  <ItemGroup>
    <Midl Include="App.idl" />
    <Midl Include="MainWindow.idl" />
    <Midl Include="BrowseVideoData.idl">
    <Midl Include="VideoGalleryData.idl">
      <Filter>Data Types</Filter>
    </Midl>
    <Midl Include="ImportEditPageState.idl">
    <Midl Include="VideoPreviewData.idl">
      <Filter>Data Types</Filter>
    </Midl>
    <Midl Include="VideoDataEditorState.idl">
    <Midl Include="VideoDetailData.idl">
      <Filter>Data Types</Filter>
    </Midl>
  </ItemGroup>
  <ItemGroup>
    <ClCompile Include="pch.cpp" />
    <ClCompile Include="$(GeneratedFilesDir)module.g.cpp" />
    <ClCompile Include="BrowseVideoData.cpp">
    <ClCompile Include="VideoGalleryData.cpp">
      <Filter>Data Types</Filter>
    </ClCompile>
    <ClCompile Include="ImportEditPageState.cpp">
    <ClCompile Include="VideoPreviewData.cpp">
      <Filter>Data Types</Filter>
    </ClCompile>
    <ClCompile Include="VideoDataEditorState.cpp">
    <ClCompile Include="VideoDetailData.cpp">
      <Filter>Data Types</Filter>
    </ClCompile>
  </ItemGroup>
  <ItemGroup>
    <ClInclude Include="pch.h" />
    <ClInclude Include="BrowseVideoData.h">
    <ClInclude Include="VideoGalleryData.h">
      <Filter>Data Types</Filter>
    </ClInclude>
    <ClInclude Include="ImportEditPageState.h">
    <ClInclude Include="VideoPreviewData.h">
      <Filter>Data Types</Filter>
    </ClInclude>
    <ClInclude Include="VideoDataEditorState.h">
    <ClInclude Include="VideoDetailData.h">
      <Filter>Data Types</Filter>
    </ClInclude>
  </ItemGroup>