Added Main Project
This commit is contained in:
@@ -0,0 +1,263 @@
|
||||
<Window x:Class="TeamsNetphoneLink.WPF.DashboardWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="clr-namespace:TeamsNetphoneLink.WPF.MVVM"
|
||||
Title="TeamsNetphoneLink Dashboard" Height="554" Width="800"
|
||||
Background="#FF1E1E1E" FontFamily="Segoe UI" ResizeMode="CanMinimize"
|
||||
WindowStartupLocation="CenterScreen">
|
||||
|
||||
<Window.DataContext>
|
||||
<local:DashboardViewModel />
|
||||
</Window.DataContext>
|
||||
|
||||
<Window.Resources>
|
||||
<!-- Button Style -->
|
||||
<Style TargetType="Button">
|
||||
<Setter Property="Background" Value="#FF0078D4"/>
|
||||
<Setter Property="Foreground" Value="White"/>
|
||||
<Setter Property="BorderBrush" Value="Transparent"/>
|
||||
<Setter Property="Padding" Value="10,5"/>
|
||||
<Setter Property="FontSize" Value="14"/>
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
<Setter Property="Cursor" Value="Hand"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Border Background="{TemplateBinding Background}"
|
||||
CornerRadius="4"
|
||||
Padding="{TemplateBinding Padding}">
|
||||
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Style.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter Property="Background" Value="#0063B1"/>
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
|
||||
<!-- Status Border Style -->
|
||||
<Style TargetType="Border" x:Key="StatusBorder">
|
||||
<Setter Property="CornerRadius" Value="12"/>
|
||||
<Setter Property="Padding" Value="10,5"/>
|
||||
<Setter Property="Background" Value="#FF252526"/>
|
||||
<Setter Property="MinWidth" Value="100"/>
|
||||
<Setter Property="HorizontalAlignment" Value="Left"/>
|
||||
</Style>
|
||||
|
||||
<!-- Status Text Style -->
|
||||
<Style TargetType="TextBlock" x:Key="StatusText">
|
||||
<Setter Property="Foreground" Value="White"/>
|
||||
<Setter Property="FontSize" Value="14"/>
|
||||
<Setter Property="VerticalAlignment" Value="Center"/>
|
||||
<Setter Property="HorizontalAlignment" Value="Center"/>
|
||||
</Style>
|
||||
|
||||
<!-- ListView Styling -->
|
||||
<Style TargetType="ListView">
|
||||
<Setter Property="Background" Value="#FF252526"/>
|
||||
<Setter Property="Foreground" Value="White"/>
|
||||
<Setter Property="BorderBrush" Value="#FF3E3E40"/>
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled"/>
|
||||
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/>
|
||||
</Style>
|
||||
|
||||
<!-- GridViewColumnHeader Style -->
|
||||
<Style TargetType="GridViewColumnHeader">
|
||||
<Setter Property="Background" Value="#FF252526"/>
|
||||
<Setter Property="Foreground" Value="White"/>
|
||||
<Setter Property="BorderBrush" Value="#FF3E3E40"/>
|
||||
<Setter Property="Padding" Value="5"/>
|
||||
<Setter Property="Height" Value="32"/>
|
||||
<Setter Property="FontSize" Value="12"/>
|
||||
<Setter Property="HorizontalContentAlignment" Value="Left"/>
|
||||
</Style>
|
||||
</Window.Resources>
|
||||
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<!-- Header -->
|
||||
<RowDefinition Height="Auto"/>
|
||||
<!-- Status Panels -->
|
||||
<RowDefinition Height="*"/>
|
||||
<!-- Log Section -->
|
||||
<RowDefinition Height="Auto"/>
|
||||
<!-- Footer -->
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- Header -->
|
||||
<StackPanel Grid.Row="0" Orientation="Horizontal" HorizontalAlignment="Center" Margin="0,15">
|
||||
<TextBlock FontSize="24" FontWeight="Bold" Text="{Binding AppTitle2}" Foreground="White"/>
|
||||
<Border Visibility="{Binding VersionVisibility}" Background="{Binding VersionBackground}" CornerRadius="12" Padding="12,4" Margin="15,0">
|
||||
<TextBlock Text="{Binding VersionText}" Foreground="White" FontSize="16"/>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Status Indicators -->
|
||||
<Grid Grid.Row="1" Margin="15,20">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- Left Status Column -->
|
||||
<StackPanel Grid.Column="0">
|
||||
<!-- Teams Local API Status -->
|
||||
<StackPanel Orientation="Horizontal" Margin="0,8">
|
||||
<TextBlock Text="Teams Local API Status" Width="180" FontWeight="Bold" Foreground="White"/>
|
||||
<Border Style="{StaticResource StatusBorder}"
|
||||
Background="{Binding TeamsLocalAPIStatusBackground}"
|
||||
MouseDown="TeamsLocalAPIBorder_MouseDown">
|
||||
<TextBlock Style="{StaticResource StatusText}" Text="{Binding TeamsLocalAPIStatusText}"/>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Teams Graph API Status -->
|
||||
<StackPanel Orientation="Horizontal" Margin="0,8">
|
||||
<TextBlock Text="Teams Graph API Status" Width="180" FontWeight="Bold" Foreground="White"/>
|
||||
<Border Style="{StaticResource StatusBorder}"
|
||||
Background="{Binding TeamsGraphAPIStatusBackground}"
|
||||
MouseDown="TeamsGraphAPIBorder_MouseDown">
|
||||
<TextBlock Style="{StaticResource StatusText}" Text="{Binding TeamsGraphAPIStatusText}"/>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
|
||||
<!-- NetPhone CLMGR Status -->
|
||||
<StackPanel Orientation="Horizontal" Margin="0,8">
|
||||
<TextBlock Text="NetPhone CLMGR Status" Width="180" FontWeight="Bold" Foreground="White"/>
|
||||
<Border Style="{StaticResource StatusBorder}"
|
||||
Background="{Binding NetphoneCLMGRStatusBackground}">
|
||||
<TextBlock Style="{StaticResource StatusText}" Text="{Binding NetphoneCLMGRStatusText}"/>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Right Status Column -->
|
||||
<StackPanel Grid.Column="1">
|
||||
<!-- Teams Meeting Status -->
|
||||
<StackPanel Orientation="Horizontal" Margin="0,8">
|
||||
<TextBlock Text="Teams Meeting" Width="180" FontWeight="Bold" Foreground="White"/>
|
||||
<Border Style="{StaticResource StatusBorder}"
|
||||
Background="{Binding TeamsMeetingStatusBackground}">
|
||||
<TextBlock Style="{StaticResource StatusText}" Text="{Binding TeamsMeetingStatusText}"/>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
|
||||
<!-- NetPhone Call Status -->
|
||||
<StackPanel Orientation="Horizontal" Margin="0,8">
|
||||
<TextBlock Text="NetPhone Call" Width="180" FontWeight="Bold" Foreground="White"/>
|
||||
<Border Style="{StaticResource StatusBorder}"
|
||||
Background="{Binding NetphoneCallStatusBackground}">
|
||||
<TextBlock Style="{StaticResource StatusText}" Text="{Binding NetphoneCallStatusText}"/>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<!-- Log Section -->
|
||||
<Border Grid.Row="2" Margin="15,0,15,10" Background="#FF252526" CornerRadius="8">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBlock Text="Activity Log"
|
||||
FontSize="18"
|
||||
FontWeight="Bold"
|
||||
Foreground="White"
|
||||
Margin="15,10"/>
|
||||
|
||||
<ListView x:Name="Log" Grid.Row="1"
|
||||
ItemsSource="{Binding LogEntries}"
|
||||
Margin="10,0,10,10"
|
||||
VirtualizingPanel.IsVirtualizing="True"
|
||||
IsHitTestVisible="False">
|
||||
<ListView.View>
|
||||
<GridView>
|
||||
<GridViewColumn Width="80" Header="Time">
|
||||
<GridViewColumn.DisplayMemberBinding>
|
||||
<Binding Path="Time" StringFormat="{}{0:HH:mm:ss}"/>
|
||||
</GridViewColumn.DisplayMemberBinding>
|
||||
</GridViewColumn>
|
||||
|
||||
<GridViewColumn Width="60" Header="Type">
|
||||
<GridViewColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding Type}" Foreground="White">
|
||||
<TextBlock.Style>
|
||||
<Style TargetType="TextBlock">
|
||||
<Setter Property="Foreground" Value="#FF0078D4"/>
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding Type}" Value="Warn">
|
||||
<Setter Property="Foreground" Value="Orange"/>
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding Type}" Value="Error">
|
||||
<Setter Property="Foreground" Value="Red"/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</TextBlock.Style>
|
||||
</TextBlock>
|
||||
</DataTemplate>
|
||||
</GridViewColumn.CellTemplate>
|
||||
</GridViewColumn>
|
||||
|
||||
<GridViewColumn Width="120" Header="Subsystem">
|
||||
<GridViewColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding Subsystem}">
|
||||
<TextBlock.Style>
|
||||
<Style TargetType="TextBlock">
|
||||
<Setter Property="Foreground" Value="White"/>
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding Subsystem}" Value="Netphone">
|
||||
<Setter Property="Foreground" Value="#E20074"/>
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding Subsystem}" Value="TeamsGraphAPI">
|
||||
<Setter Property="Foreground" Value="#4E5FBF"/>
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding Subsystem}" Value="TeamsLocalAPI">
|
||||
<Setter Property="Foreground" Value="#4E5FBF"/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</TextBlock.Style>
|
||||
</TextBlock>
|
||||
</DataTemplate>
|
||||
</GridViewColumn.CellTemplate>
|
||||
</GridViewColumn>
|
||||
|
||||
<GridViewColumn Width="Auto" Header="Message"
|
||||
DisplayMemberBinding="{Binding Message}"/>
|
||||
</GridView>
|
||||
</ListView.View>
|
||||
</ListView>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<!-- Footer -->
|
||||
<StackPanel Grid.Row="3" Orientation="Horizontal" Margin="15,0,0,8">
|
||||
<Button Click="SettingsButton_Click"
|
||||
Padding="8"
|
||||
FontSize="18"
|
||||
VerticalAlignment="Center"
|
||||
ToolTip="Settings">
|
||||
<Button.Content>
|
||||
<Path Data="M12,15.5A3.5,3.5 0 0,1 8.5,12A3.5,3.5 0 0,1 12,8.5A3.5,3.5 0 0,1 15.5,12A3.5,3.5 0 0,1 12,15.5M19.43,12.97C19.47,12.65 19.5,12.33 19.5,12C19.5,11.67 19.47,11.34 19.43,11L21.54,9.37C21.73,9.22 21.78,8.95 21.66,8.73L19.66,5.27C19.54,5.05 19.27,4.96 19.05,5.05L16.56,6.05C16.04,5.66 15.5,5.32 14.87,5.07L14.5,2.42C14.46,2.18 14.25,2 14,2H10C9.75,2 9.54,2.18 9.5,2.42L9.13,5.07C8.5,5.32 7.96,5.66 7.44,6.05L4.95,5.05C4.73,4.96 4.46,5.05 4.34,5.27L2.34,8.73C2.21,8.95 2.27,9.22 2.46,9.37L4.57,11C4.53,11.34 4.5,11.67 4.5,12C4.5,12.33 4.53,12.65 4.57,12.97L2.46,14.63C2.27,14.78 2.21,15.05 2.34,15.27L4.34,18.73C4.46,18.95 4.73,19.03 4.95,18.95L7.44,17.94C7.96,18.34 8.5,18.68 9.13,18.93L9.5,21.58C9.54,21.82 9.75,22 10,22H14C14.25,22 14.46,21.82 14.5,21.58L14.87,18.93C15.5,18.68 16.04,18.34 16.56,17.94L19.05,18.95C19.27,19.03 19.54,18.95 19.66,18.73L21.66,15.27C21.78,15.05 21.73,14.78 21.54,14.63L19.43,12.97Z"
|
||||
Fill="White" Stretch="Uniform"/>
|
||||
</Button.Content>
|
||||
</Button>
|
||||
|
||||
<TextBlock Text="Entwickelt von Jan Krampitz"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="White"
|
||||
FontSize="10"
|
||||
Margin="15,0"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Window>
|
||||
@@ -0,0 +1,95 @@
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
using TeamsNetphoneLink.WPF.MVVM;
|
||||
|
||||
namespace TeamsNetphoneLink.WPF
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for MainWindow.xaml
|
||||
/// </summary>
|
||||
///
|
||||
public partial class DashboardWindow : Window
|
||||
{
|
||||
private bool isDarkMode = true;
|
||||
private Syncronisation sync;
|
||||
private Task<bool> graphAuthenticationTask;
|
||||
|
||||
public DashboardViewModel _dashboardViewModel { get; }
|
||||
|
||||
public DashboardWindow(Syncronisation _syncronisation)
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
sync = _syncronisation;
|
||||
_dashboardViewModel = sync._dashboardViewModel;
|
||||
DataContext = _dashboardViewModel;
|
||||
// Set the ViewModel instance & Set the DataContext to the ViewModel
|
||||
//Set the DataContext to the ViewModel
|
||||
Log.DataContext = sync.LogEntries;
|
||||
}
|
||||
|
||||
private void ToggleDarkMode(object sender, RoutedEventArgs e)
|
||||
{
|
||||
isDarkMode = !isDarkMode;
|
||||
|
||||
if (isDarkMode)
|
||||
{
|
||||
this.Style = (Style)FindResource("DarkModeStyle");
|
||||
}
|
||||
else
|
||||
{
|
||||
this.Style = (Style)FindResource("LightModeStyle");
|
||||
}
|
||||
}
|
||||
|
||||
private void SettingsButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// Einstellungsfenster öffnen
|
||||
SettingsWindow settingsWindow = new SettingsWindow(sync);
|
||||
settingsWindow.ShowDialog();
|
||||
}
|
||||
|
||||
private async void TeamsGraphAPIBorder_MouseDown(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
if (!Teams.TeamsGraph.IsGuid(Settings.Default.AppID) || !Teams.TeamsGraph.IsGuid(Settings.Default.TenantID))
|
||||
{
|
||||
SettingsWindow settingsWindow = new SettingsWindow(sync);
|
||||
settingsWindow.ShowDialog();
|
||||
return;
|
||||
};
|
||||
|
||||
if (sync.TeamsGraphAuthenticationSemaphore.CurrentCount == 0)
|
||||
return;
|
||||
|
||||
bool saveCredentials = Settings.Default.SaveEntraCredentials;
|
||||
if (!sync.TeamsGraph.Authenticated)
|
||||
{
|
||||
await sync.TeamsGraphAuthenticationSemaphore.WaitAsync();
|
||||
_ = await sync.GraphAuthenticate(saveCredentials);
|
||||
sync.TeamsGraphAuthenticationSemaphore.Release();
|
||||
}
|
||||
}
|
||||
|
||||
private void TeamsLocalAPIBorder_MouseDown(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
if (!Settings.Default.TeamsPermission)
|
||||
{
|
||||
var setupLocalTeams = new WPF.SetupLocalTeamsAPI(sync);
|
||||
setupLocalTeams.ShowDialog();
|
||||
}
|
||||
}
|
||||
|
||||
private void LogListView_UpdateColumnsWidth(object sender, SizeChangedEventArgs e)
|
||||
{
|
||||
ListView _ListView = sender as ListView;
|
||||
GridView _GridView = _ListView.View as GridView;
|
||||
var _ActualWidth = _ListView.ActualWidth - SystemParameters.VerticalScrollBarWidth;
|
||||
for (Int32 i = 1; i < _GridView.Columns.Count; i++)
|
||||
{
|
||||
_ActualWidth = _ActualWidth - _GridView.Columns[i].ActualWidth;
|
||||
}
|
||||
_GridView.Columns[0].Width = _ActualWidth;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
<Window x:Class="TeamsNetphoneLink.WPF.ExceptionWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
Title="Fehlerbericht" Height="400" Width="600"
|
||||
Background="#FF1E1E1E" FontFamily="Segoe UI"
|
||||
WindowStyle="ToolWindow" ResizeMode="NoResize"
|
||||
WindowStartupLocation="CenterScreen" Topmost="True">
|
||||
|
||||
<!-- Dark Mode Resources -->
|
||||
<Window.Resources>
|
||||
<!-- Button Style -->
|
||||
<Style TargetType="Button">
|
||||
<Setter Property="Background" Value="#FF0078D4"/>
|
||||
<Setter Property="Foreground" Value="White"/>
|
||||
<Setter Property="BorderBrush" Value="Transparent"/>
|
||||
<Setter Property="Padding" Value="10,5"/>
|
||||
<Setter Property="FontSize" Value="14"/>
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
<Setter Property="Cursor" Value="Hand"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Border Background="{TemplateBinding Background}"
|
||||
CornerRadius="4"
|
||||
Padding="{TemplateBinding Padding}">
|
||||
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Setter Property="OverridesDefaultStyle" Value="True"/>
|
||||
</Style>
|
||||
|
||||
<!-- TextBox Style -->
|
||||
<Style TargetType="TextBox">
|
||||
<Setter Property="Background" Value="#FF252526"/>
|
||||
<Setter Property="Foreground" Value="White"/>
|
||||
<Setter Property="BorderBrush" Value="#FF3E3E40"/>
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
<Setter Property="Padding" Value="3,0,0,0"/>
|
||||
<Setter Property="FontSize" Value="14"/>
|
||||
<Setter Property="VerticalContentAlignment" Value="Center"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="TextBox">
|
||||
<Border Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
CornerRadius="4">
|
||||
<ScrollViewer x:Name="PART_ContentHost" Margin="{TemplateBinding Padding}"/>
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<!-- TextBlock Style -->
|
||||
<Style TargetType="TextBlock">
|
||||
<Setter Property="Foreground" Value="White"/>
|
||||
<Setter Property="FontSize" Value="14"/>
|
||||
<Setter Property="VerticalAlignment" Value="Center"/>
|
||||
</Style>
|
||||
</Window.Resources>
|
||||
|
||||
<Grid Margin="10">
|
||||
<!-- Fehlertext -->
|
||||
<TextBlock x:Name="ErrorText"
|
||||
Text="Es ist ein Fehler aufgetreten!"
|
||||
FontSize="18"
|
||||
FontWeight="Bold"
|
||||
Margin="10,10,10,20"
|
||||
VerticalAlignment="Top"
|
||||
Foreground="White"/>
|
||||
|
||||
<!-- Modul Feld -->
|
||||
<StackPanel Orientation="Horizontal" Margin="10,50,10,0">
|
||||
<TextBlock Text="Modul:" VerticalAlignment="Top" Width="60" Foreground="White" Margin="0,15,0,0"/>
|
||||
<TextBox x:Name="ModuleText"
|
||||
FontSize="14"
|
||||
VerticalAlignment="Top"
|
||||
Width="480"
|
||||
Margin="5,5,5,0"
|
||||
IsEnabled="False"
|
||||
Background="#FF252526"
|
||||
Foreground="White" Text="tetet" Height="37"/>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Scrollbare Fehlerdetails -->
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto"
|
||||
HorizontalScrollBarVisibility="Auto"
|
||||
Margin="10,100,10,50">
|
||||
<TextBox x:Name="TraceText"
|
||||
VerticalScrollBarVisibility="Auto"
|
||||
HorizontalScrollBarVisibility="Auto"
|
||||
IsReadOnly="True"
|
||||
FontSize="14"
|
||||
Padding="3,3,0,0"
|
||||
HorizontalContentAlignment="Left"
|
||||
VerticalContentAlignment="Top"
|
||||
Height="214"
|
||||
Background="#FF252526"
|
||||
Foreground="White" Text="tttt">
|
||||
<!-- Beispiel für Fehlerdetails -->
|
||||
</TextBox>
|
||||
</ScrollViewer>
|
||||
|
||||
<!-- Button zum Schließen -->
|
||||
<Button Content="Schließen"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Bottom"
|
||||
Width="100"
|
||||
Margin="10,0,0,10"
|
||||
Click="CloseButton_Click"/>
|
||||
|
||||
<!-- Button zum Issue melden -->
|
||||
<Button Content="Issue melden"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Bottom"
|
||||
Width="120"
|
||||
Margin="0,0,10,10"
|
||||
Click="ReportButton_Click"/>
|
||||
</Grid>
|
||||
</Window>
|
||||
@@ -0,0 +1,67 @@
|
||||
using System.Diagnostics;
|
||||
using System.Media;
|
||||
using System.Windows;
|
||||
|
||||
namespace TeamsNetphoneLink.WPF
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaktionslogik für ExceptionWindow.xaml
|
||||
/// </summary>
|
||||
public partial class ExceptionWindow : Window
|
||||
{
|
||||
public ExceptionWindow(string module, string trace)
|
||||
{
|
||||
InitializeComponent();
|
||||
ModuleText.Text = module;
|
||||
TraceText.Text = trace;
|
||||
SystemSounds.Hand.Play();
|
||||
}
|
||||
|
||||
// Button zum Schließen
|
||||
private void CloseButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
this.Close();
|
||||
}
|
||||
|
||||
// Button zum Issue melden
|
||||
private void ReportButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// Fehlerdetails aus der TextBox holen
|
||||
string moduleDetails = ModuleText.Text;
|
||||
string errorDetails = TraceText.Text;
|
||||
|
||||
// URL für das GitHub Issue, Fehlerdetails werden als Parameter angehängt
|
||||
string url = $"https://git.jan.sx/krjan02/TeamsNetphoneLink/issues/new?body={Uri.EscapeDataString(String.Format("Modul: {0}\n\nTrace:\n{1}", moduleDetails, errorDetails))}";
|
||||
|
||||
// GitHub im Standardbrowser öffnen
|
||||
Process.Start(new ProcessStartInfo(url) { UseShellExecute = true });
|
||||
}
|
||||
}
|
||||
|
||||
public static class ExceptionWindowHelper
|
||||
{
|
||||
public static void Show(string classname, string method, string message, string trace)
|
||||
{
|
||||
var modulestr = String.Format("{0} - {1}", classname, method);
|
||||
var errorstr = String.Format("{0}\n{1}", message, trace);
|
||||
|
||||
// Use the Dispatcher to show the window on the UI thread
|
||||
Application.Current.Dispatcher.InvokeAsync(() =>
|
||||
{
|
||||
new ExceptionWindow(modulestr, errorstr).Show();
|
||||
});
|
||||
}
|
||||
|
||||
public static void Show(string classname, string method, string message, string tracemessage, string trace)
|
||||
{
|
||||
var modulestr = String.Format("{0} - {1}", classname, method);
|
||||
var errorstr = String.Format("{0}\n{1}\n{2}", message, tracemessage, trace);
|
||||
|
||||
// Use the Dispatcher to show the window on the UI thread
|
||||
Application.Current.Dispatcher.InvokeAsync(() =>
|
||||
{
|
||||
new ExceptionWindow(modulestr, errorstr).Show();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,250 @@
|
||||
using System.ComponentModel;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace TeamsNetphoneLink.WPF.MVVM
|
||||
{
|
||||
public static class StatusPanelNames
|
||||
{
|
||||
public const string TeamsLocalAPIStatus = nameof(TeamsLocalAPIStatus);
|
||||
public const string TeamsGraphAPIStatus = nameof(TeamsGraphAPIStatus);
|
||||
public const string NetphoneCLMGRStatus = nameof(NetphoneCLMGRStatus);
|
||||
public const string TeamsMeetingStatus = nameof(TeamsMeetingStatus);
|
||||
public const string NetphoneCallStatus = nameof(NetphoneCallStatus);
|
||||
}
|
||||
|
||||
public class DashboardViewModel : INotifyPropertyChanged
|
||||
{
|
||||
// Parameterloser Konstruktor
|
||||
public DashboardViewModel()
|
||||
{
|
||||
// Initialisieren Sie hier Standardwerte, falls erforderlich
|
||||
TeamsLocalAPIStatusText = "Nicht verbunden";
|
||||
TeamsLocalAPIStatusBackground = new SolidColorBrush(Colors.Red);
|
||||
TeamsGraphAPIStatusText = "Konfiguration fehlt";
|
||||
TeamsGraphAPIStatusBackground = new SolidColorBrush(Colors.Red);
|
||||
NetphoneCLMGRStatusText = "Nicht verbunden";
|
||||
NetphoneCLMGRStatusBackground = new SolidColorBrush(Colors.Red);
|
||||
TeamsMeetingStatusText = "Not Active";
|
||||
TeamsMeetingStatusBackground = new SolidColorBrush(Colors.DarkOrange);
|
||||
NetphoneCallStatusText = "Not Active";
|
||||
NetphoneCallStatusBackground = new SolidColorBrush(Colors.DarkOrange);
|
||||
|
||||
AppTitle2 = "Teams Netphone Link";
|
||||
VersionText = "Aktuell";
|
||||
VersionVisibility = Visibility.Visible;
|
||||
VersionBackground = new SolidColorBrush(Colors.Green);
|
||||
}
|
||||
|
||||
|
||||
private string _appTitle;
|
||||
|
||||
private string _versionText;
|
||||
private Visibility _versionVisibility;
|
||||
private Brush _versionBackground;
|
||||
|
||||
private string _teamsLocalAPIStatusText;
|
||||
private Brush _teamsLocalAPIStatusBackground;
|
||||
private string _teamsGraphAPIStatusText;
|
||||
private Brush _teamsGraphAPIStatusBackground;
|
||||
private string _netphoneCLMGRStatusText;
|
||||
private Brush _netphoneCLMGRStatusBackground;
|
||||
private string _teamsMeetingStatusText;
|
||||
private Brush _teamsMeetingStatusBackground;
|
||||
private string _netphoneCallStatusText;
|
||||
private Brush _netphoneCallStatusBackground;
|
||||
|
||||
public string TeamsLocalAPIStatusText
|
||||
{
|
||||
get => _teamsLocalAPIStatusText;
|
||||
set
|
||||
{
|
||||
_teamsLocalAPIStatusText = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public Brush TeamsLocalAPIStatusBackground
|
||||
{
|
||||
get => _teamsLocalAPIStatusBackground;
|
||||
set
|
||||
{
|
||||
_teamsLocalAPIStatusBackground = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public string TeamsGraphAPIStatusText
|
||||
{
|
||||
get => _teamsGraphAPIStatusText;
|
||||
set
|
||||
{
|
||||
_teamsGraphAPIStatusText = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public Brush TeamsGraphAPIStatusBackground
|
||||
{
|
||||
get => _teamsGraphAPIStatusBackground;
|
||||
set
|
||||
{
|
||||
_teamsGraphAPIStatusBackground = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public string NetphoneCLMGRStatusText
|
||||
{
|
||||
get => _netphoneCLMGRStatusText;
|
||||
set
|
||||
{
|
||||
_netphoneCLMGRStatusText = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public Brush NetphoneCLMGRStatusBackground
|
||||
{
|
||||
get => _netphoneCLMGRStatusBackground;
|
||||
set
|
||||
{
|
||||
_netphoneCLMGRStatusBackground = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public string TeamsMeetingStatusText
|
||||
{
|
||||
get => _teamsMeetingStatusText;
|
||||
set
|
||||
{
|
||||
_teamsMeetingStatusText = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public Brush TeamsMeetingStatusBackground
|
||||
{
|
||||
get => _teamsMeetingStatusBackground;
|
||||
set
|
||||
{
|
||||
_teamsMeetingStatusBackground = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public string NetphoneCallStatusText
|
||||
{
|
||||
get => _netphoneCallStatusText;
|
||||
set
|
||||
{
|
||||
_netphoneCallStatusText = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public Brush NetphoneCallStatusBackground
|
||||
{
|
||||
get => _netphoneCallStatusBackground;
|
||||
set
|
||||
{
|
||||
_netphoneCallStatusBackground = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public string AppTitle2
|
||||
{
|
||||
get => _appTitle;
|
||||
set
|
||||
{
|
||||
Application.Current.Dispatcher.InvokeAsync(() =>
|
||||
{
|
||||
_appTitle = value;
|
||||
OnPropertyChanged();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public string VersionText
|
||||
{
|
||||
get => _versionText;
|
||||
set
|
||||
{
|
||||
Application.Current.Dispatcher.InvokeAsync(() =>
|
||||
{
|
||||
_versionText = value;
|
||||
OnPropertyChanged();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public Visibility VersionVisibility
|
||||
{
|
||||
get => _versionVisibility;
|
||||
set
|
||||
{
|
||||
Application.Current.Dispatcher.InvokeAsync(() =>
|
||||
{
|
||||
_versionVisibility = value;
|
||||
OnPropertyChanged();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public Brush VersionBackground
|
||||
{
|
||||
get => _versionBackground;
|
||||
set
|
||||
{
|
||||
Application.Current.Dispatcher.InvokeAsync(() =>
|
||||
{
|
||||
_versionBackground = value;
|
||||
OnPropertyChanged();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
|
||||
// Method to update status panel
|
||||
public void UpdateStatusPanel(string statusPanelName, Color color, string text)
|
||||
{
|
||||
Application.Current.Dispatcher.InvokeAsync(() =>
|
||||
{
|
||||
// Get the type of the ViewModel
|
||||
var type = GetType();
|
||||
|
||||
// Construct property names for Text and Background
|
||||
string textPropertyName = $"{statusPanelName}Text";
|
||||
string backgroundPropertyName = $"{statusPanelName}Background";
|
||||
|
||||
// Get the properties using reflection
|
||||
PropertyInfo textProperty = type.GetProperty(textPropertyName); //CS8600
|
||||
PropertyInfo backgroundProperty = type.GetProperty(backgroundPropertyName); //CS8600
|
||||
|
||||
if (textProperty != null && backgroundProperty != null)
|
||||
{
|
||||
// Update the Text property
|
||||
textProperty.SetValue(this, text);
|
||||
|
||||
// Update the Background property
|
||||
backgroundProperty.SetValue(this, new SolidColorBrush(color));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentException($"Invalid status panel name: {statusPanelName}");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
using Emoji.Wpf;
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Effects;
|
||||
using System.Windows.Threading;
|
||||
|
||||
namespace TeamsNetphoneLink.WPF.MVVM
|
||||
{
|
||||
public class FinishPanelViewModel : INotifyPropertyChanged
|
||||
{
|
||||
// Parameterloser Konstruktor
|
||||
public FinishPanelViewModel()
|
||||
{
|
||||
// Initialisieren Sie hier Standardwerte, falls erforderlich
|
||||
FinishPanelEnabled = false;
|
||||
FinishPanelFinishTextColor = new SolidColorBrush(Colors.DarkOrange);
|
||||
FinishPanelFinishTextText = "Authentifizierung ausstehend...";
|
||||
FinishPanelEffect = new TintEffect { Tint = Colors.DarkGray };
|
||||
}
|
||||
|
||||
private bool _finishPanelEnabled;
|
||||
private Brush _finishPanelFinishTextColor;
|
||||
private string _finishPanelFinishTextText;
|
||||
private Effect _finishPanelEffect;
|
||||
|
||||
public bool FinishPanelEnabled
|
||||
{
|
||||
get => _finishPanelEnabled;
|
||||
set
|
||||
{
|
||||
System.Windows.Application.Current.Dispatcher.Invoke(delegate
|
||||
{
|
||||
_finishPanelEnabled = value;
|
||||
OnPropertyChanged();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public Brush FinishPanelFinishTextColor
|
||||
{
|
||||
get => _finishPanelFinishTextColor;
|
||||
set
|
||||
{
|
||||
System.Windows.Application.Current.Dispatcher.Invoke(delegate
|
||||
{
|
||||
_finishPanelFinishTextColor = value;
|
||||
OnPropertyChanged();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public string FinishPanelFinishTextText
|
||||
{
|
||||
get => _finishPanelFinishTextText;
|
||||
set
|
||||
{
|
||||
System.Windows.Application.Current.Dispatcher.Invoke(delegate
|
||||
{
|
||||
_finishPanelFinishTextText = value;
|
||||
OnPropertyChanged();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public Effect FinishPanelEffect
|
||||
{
|
||||
get => _finishPanelEffect;
|
||||
set
|
||||
{
|
||||
System.Windows.Application.Current.Dispatcher.Invoke(delegate
|
||||
{
|
||||
_finishPanelEffect = value;
|
||||
OnPropertyChanged();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void SetFinishPanelFinishTextColor(Color color)
|
||||
{
|
||||
System.Windows.Application.Current.Dispatcher.Invoke(delegate
|
||||
{
|
||||
FinishPanelFinishTextColor = new SolidColorBrush((Color)color);
|
||||
});
|
||||
}
|
||||
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Windows;
|
||||
|
||||
|
||||
namespace TeamsNetphoneLink.WPF.MVVM
|
||||
{
|
||||
public class LogEntry
|
||||
{
|
||||
public DateTime Time { get; set; }
|
||||
public string Subsystem { get; set; }
|
||||
public string Type { get; set; }
|
||||
public string Message { get; set; }
|
||||
}
|
||||
|
||||
public class LogViewModel : INotifyPropertyChanged
|
||||
{
|
||||
// Collection to hold log entries
|
||||
public ObservableCollection<LogEntry> LogEntries { get; } = new ObservableCollection<LogEntry>();
|
||||
|
||||
// Add a log entry
|
||||
public void AddLogEntry(string entry)
|
||||
{
|
||||
Application.Current.Dispatcher.InvokeAsync(() =>
|
||||
{
|
||||
LogEntries.Add(new LogEntry { Time = DateTime.Now, Type = "Info", Subsystem = "NONE", Message = entry });
|
||||
});
|
||||
}
|
||||
|
||||
// Add a log entry
|
||||
public void AddLogEntry(string subsystem, string entry)
|
||||
{
|
||||
Application.Current.Dispatcher.InvokeAsync(() =>
|
||||
{
|
||||
LogEntries.Add(new LogEntry { Time = DateTime.Now, Type = "Info", Subsystem = subsystem, Message = entry });
|
||||
});
|
||||
}
|
||||
|
||||
// Add a log entry
|
||||
public void AddLogEntry(string type, string subsystem, string entry)
|
||||
{
|
||||
Application.Current.Dispatcher.InvokeAsync(() =>
|
||||
{
|
||||
LogEntries.Add(new LogEntry { Time = DateTime.Now, Type = type, Subsystem = subsystem, Message = entry });
|
||||
});
|
||||
}
|
||||
|
||||
public void AddLogEntry(DateTime time, string subsystem, string message)
|
||||
{
|
||||
Application.Current.Dispatcher.InvokeAsync(() =>
|
||||
{
|
||||
LogEntries.Add(new LogEntry { Time = time, Type = "Info", Subsystem = subsystem, Message = message });
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// Clear all log entries
|
||||
public void ClearLogEntries()
|
||||
{
|
||||
Application.Current.Dispatcher.InvokeAsync(() =>
|
||||
{
|
||||
LogEntries.Clear();
|
||||
});
|
||||
}
|
||||
|
||||
// Implement INotifyPropertyChanged
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
<Window x:Class="TeamsNetphoneLink.WPF.SettingsWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
Title="Einstellungen" Height="373" Width="500"
|
||||
WindowStyle="ToolWindow" ResizeMode="NoResize"
|
||||
Background="#FF1E1E1E" FontFamily="Segoe UI"
|
||||
WindowStartupLocation="CenterScreen">
|
||||
|
||||
<!-- Dark Mode Resources -->
|
||||
<Window.Resources>
|
||||
<!-- Button Style -->
|
||||
<Style TargetType="Button">
|
||||
<Setter Property="Background" Value="#FF0078D4"/>
|
||||
<Setter Property="Foreground" Value="White"/>
|
||||
<Setter Property="BorderBrush" Value="Transparent"/>
|
||||
<Setter Property="Padding" Value="10,5"/>
|
||||
<Setter Property="FontSize" Value="14"/>
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
<Setter Property="Cursor" Value="Hand"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Border x:Name="border"
|
||||
Background="{TemplateBinding Background}"
|
||||
CornerRadius="4"
|
||||
Padding="{TemplateBinding Padding}"
|
||||
Opacity="1">
|
||||
<ContentPresenter HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<!-- Hover Effect -->
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter TargetName="border" Property="Background" Value="#0066B3"/>
|
||||
</Trigger>
|
||||
|
||||
<!-- Pressed Effect -->
|
||||
<Trigger Property="IsPressed" Value="True">
|
||||
<Setter TargetName="border" Property="Background" Value="#004D86"/>
|
||||
<Setter TargetName="border" Property="RenderTransform">
|
||||
<Setter.Value>
|
||||
<ScaleTransform ScaleX="0.98" ScaleY="0.98"/>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Trigger>
|
||||
|
||||
<!-- Disabled State -->
|
||||
<Trigger Property="IsEnabled" Value="False">
|
||||
<Setter TargetName="border" Property="Background" Value="#3A3A3A"/>
|
||||
<Setter TargetName="border" Property="Opacity" Value="0.7"/>
|
||||
<Setter Property="Foreground" Value="#A0A0A0"/>
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Setter Property="OverridesDefaultStyle" Value="True"/>
|
||||
</Style>
|
||||
|
||||
<!-- TextBox Style -->
|
||||
<Style TargetType="TextBox">
|
||||
<Setter Property="Background" Value="#FF252526"/>
|
||||
<Setter Property="Foreground" Value="White"/>
|
||||
<Setter Property="BorderBrush" Value="#FF3E3E40"/>
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
<Setter Property="FontSize" Value="14"/>
|
||||
<Setter Property="Padding" Value="3,0,0,0"/>
|
||||
<Setter Property="VerticalContentAlignment" Value="Center"/>
|
||||
<Setter Property="Validation.ErrorTemplate">
|
||||
<Setter.Value>
|
||||
<ControlTemplate>
|
||||
<Border BorderBrush="Red" BorderThickness="1">
|
||||
<AdornedElementPlaceholder/>
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="TextBox">
|
||||
<Border Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
CornerRadius="4">
|
||||
<ScrollViewer x:Name="PART_ContentHost" Margin="0,0,0,0"/>
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<!-- CheckBox Style -->
|
||||
<Style TargetType="CheckBox">
|
||||
<Setter Property="Foreground" Value="White"/>
|
||||
<Setter Property="FontSize" Value="14"/>
|
||||
<Setter Property="VerticalAlignment" Value="Center"/>
|
||||
</Style>
|
||||
|
||||
<!-- TextBlock Style -->
|
||||
<Style TargetType="TextBlock">
|
||||
<Setter Property="Foreground" Value="White"/>
|
||||
<Setter Property="FontSize" Value="14"/>
|
||||
<Setter Property="VerticalAlignment" Value="Center"/>
|
||||
</Style>
|
||||
</Window.Resources>
|
||||
|
||||
<Grid Margin="20">
|
||||
<StackPanel Orientation="Vertical">
|
||||
<!-- Title -->
|
||||
<TextBlock Text="Einstellungen" FontSize="24" FontWeight="SemiBold" Margin="0,0,0,20"/>
|
||||
|
||||
<!-- Microsoft 365 Tenant ID -->
|
||||
<StackPanel Orientation="Horizontal" Margin="0,10">
|
||||
<TextBlock Text="Tenant ID" VerticalAlignment="Center" Width="70" FontSize="14"/>
|
||||
<TextBox x:Name="TenantIDTextBox" Width="372" Height="30" FontSize="14" TextChanged="TextBox_TextChanged" Text="dddddd"/>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Microsoft 365 App ID -->
|
||||
<StackPanel Orientation="Horizontal" Margin="0,10">
|
||||
<TextBlock Text="App ID" VerticalAlignment="Center" Width="70" FontSize="14"/>
|
||||
<TextBox x:Name="AppIDTextBox" Width="372" Height="30" TextChanged="TextBox_TextChanged"/>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Toggle SaveEntraCredentials -->
|
||||
<StackPanel Orientation="Horizontal" Margin="0,10">
|
||||
<CheckBox x:Name="SaveEntraCredentialsCheck" VerticalAlignment="Center"/>
|
||||
<TextBlock Text="Entra Login speichern" Width="137" FontSize="14" Margin="10,0,0,0"/>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Toggle UseGraphForMeetingState -->
|
||||
<StackPanel Orientation="Horizontal" Margin="0,10" ToolTip="Nutzt statt der lokalen Teams API die Graph API um festzustellen das ein Meeting läuft">
|
||||
<CheckBox x:Name="UseGraphForMeetingStateCheck"/>
|
||||
<TextBlock Text="Graph für den Meeting Status verwenden" VerticalAlignment="Center" Width="259" FontSize="14" Margin="10,0,0,0"/>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Save and Reset Buttons -->
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,30,0,0">
|
||||
<Button x:Name="ResetButton" Background="DarkRed" Content="Alles zurücksetzen" Width="140" Height="35" Margin="0,0,10,0" Click="ResetButton_Click"/>
|
||||
<Button x:Name="SaveButton" Content="Speichern" Width="100" Height="35" Click="SaveButton_Click" IsEnabled="False"/>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Window>
|
||||
@@ -0,0 +1,132 @@
|
||||
using Microsoft.Identity.Client.NativeInterop;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Shapes;
|
||||
using TeamsNetphoneLink;
|
||||
|
||||
namespace TeamsNetphoneLink.WPF
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaktionslogik für SettingsWindow.xaml
|
||||
/// </summary>
|
||||
public partial class SettingsWindow : Window
|
||||
{
|
||||
Syncronisation _sync;
|
||||
|
||||
public SettingsWindow(Syncronisation sync)
|
||||
{
|
||||
_sync = sync;
|
||||
InitializeComponent();
|
||||
TenantIDTextBox.Text = Settings.Default.TenantID;
|
||||
AppIDTextBox.Text = Settings.Default.AppID;
|
||||
SaveEntraCredentialsCheck.IsChecked = Settings.Default.SaveEntraCredentials;
|
||||
UseGraphForMeetingStateCheck.IsChecked = Settings.Default.UseGraphForMeetingState;
|
||||
}
|
||||
|
||||
private void RestartApplication()
|
||||
{
|
||||
string exePath = Process.GetCurrentProcess().MainModule.FileName;
|
||||
var startInfo = new ProcessStartInfo
|
||||
{
|
||||
FileName = exePath,
|
||||
UseShellExecute = true
|
||||
};
|
||||
Process.Start(startInfo);
|
||||
Application.Current.Shutdown();
|
||||
}
|
||||
|
||||
// Speichern der Einstellungen
|
||||
private void SaveButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
string tenantId = TenantIDTextBox.Text.Replace(" ", "");
|
||||
string appId = AppIDTextBox.Text.Replace(" ", "");
|
||||
bool SaveEntraCredentials = SaveEntraCredentialsCheck.IsChecked.HasValue ? SaveEntraCredentialsCheck.IsChecked.Value : false;
|
||||
bool UseGraphForMeetingState = UseGraphForMeetingStateCheck.IsChecked.HasValue ? UseGraphForMeetingStateCheck.IsChecked.Value : false;
|
||||
|
||||
if (SaveEntraCredentials && !ShowSecurityWarningMessage())
|
||||
return;
|
||||
|
||||
Settings.Default.TenantID = tenantId;
|
||||
Settings.Default.AppID = appId;
|
||||
Settings.Default.SaveEntraCredentials = SaveEntraCredentials;
|
||||
Settings.Default.UseGraphForMeetingState = UseGraphForMeetingState;
|
||||
Settings.Default.Save();
|
||||
|
||||
RestartApplication();
|
||||
}
|
||||
|
||||
// Reset der Einstellungen
|
||||
private void ResetButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Settings.Default.Reset();
|
||||
RestartApplication();
|
||||
}
|
||||
|
||||
private void SaveEntraCredentialsCheck_Checked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
//ShowSecurityWarningMessage();
|
||||
}
|
||||
|
||||
public bool ShowSecurityWarningMessage()
|
||||
{
|
||||
string message =
|
||||
"Die Anmeldedaten werden mit dem MSAL Cache unter Verwendung von DPAPI (Data Protection API) gespeichert. " +
|
||||
"DPAPI bietet eine sichere Verschlüsselung der Daten auf Benutzerebene, sodass die Anmeldedaten nur für das Benutzerkonto zugänglich sind, " +
|
||||
"unter dem sie gespeichert wurden.\n\n" +
|
||||
"Allerdings ist zu beachten, dass theoretisch jede Anwendung mit derselben App-ID auf den MSAL Cache zugreifen könnte. " +
|
||||
"Das liegt daran, dass der Cache anhand der App-ID identifiziert wird. Wenn eine andere Anwendung dieselbe App-ID verwendet, " +
|
||||
"könnte sie versuchen, auf die gespeicherten Anmeldedaten zuzugreifen.\n\n" +
|
||||
"Bewertung des Sicherheitsrisikos:\n" +
|
||||
"- In einer vertrauenswürdigen Umgebung ist das Risiko gering.\n" +
|
||||
"- Die App-ID sollte geheim gehalten werden, um unbefugten Zugriff zu verhindern.\n" +
|
||||
"- DPAPI bietet eine starke Verschlüsselung, solange das Benutzerkonto nicht kompromittiert ist.\n\n" +
|
||||
"Unter normalen Umständen stellt die Verwendung des MSAL Caches kein signifikantes Sicherheitsrisiko dar. " +
|
||||
"Es ist jedoch wichtig, die App-ID geheim zu halten und sicherzustellen, dass nur vertrauenswürdige Anwendungen dieselbe App-ID verwenden.\n\n" +
|
||||
"Sollen die Einstellungen gespeichert werden?.\n\n";
|
||||
|
||||
var result = MessageBox.Show(
|
||||
message,
|
||||
"Sicherheitshinweis zur Speicherung von Anmeldedaten",
|
||||
MessageBoxButton.YesNo,
|
||||
MessageBoxImage.Warning
|
||||
);
|
||||
|
||||
if (result == MessageBoxResult.Yes)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
|
||||
{
|
||||
ValidateInput();
|
||||
}
|
||||
|
||||
private void ValidateInput()
|
||||
{
|
||||
if (this.IsInitialized)
|
||||
{
|
||||
bool isAppIDValid = Teams.TeamsGraph.IsGuid(AppIDTextBox.Text);
|
||||
bool isTenantIDValid = Teams.TeamsGraph.IsGuid(TenantIDTextBox.Text);
|
||||
|
||||
// Set border color based on validation
|
||||
AppIDTextBox.BorderBrush = isAppIDValid ? System.Windows.Media.Brushes.Green : System.Windows.Media.Brushes.Red;
|
||||
TenantIDTextBox.BorderBrush = isTenantIDValid ? System.Windows.Media.Brushes.Green : System.Windows.Media.Brushes.Red;
|
||||
|
||||
// Enable Save button only if both fields are valid
|
||||
SaveButton.IsEnabled = isAppIDValid && isTenantIDValid;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
<Window
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:Wpf="clr-namespace:Emoji.Wpf;assembly=Emoji.Wpf" xmlns:local="clr-namespace:TeamsNetphoneLink.WPF.MVVM" x:Class="TeamsNetphoneLink.WPF.SetupLocalTeamsAPI"
|
||||
WindowStyle="ToolWindow" ResizeMode="NoResize"
|
||||
Background="#FF1E1E1E" FontFamily="Segoe UI"
|
||||
Title="Teams API Integration" Height="497" Width="525">
|
||||
|
||||
<Window.Resources>
|
||||
<Style TargetType="Button">
|
||||
<Setter Property="Background" Value="#FF0078D4"/>
|
||||
<Setter Property="Foreground" Value="White"/>
|
||||
<Setter Property="BorderBrush" Value="Transparent"/>
|
||||
<Setter Property="Padding" Value="10,5"/>
|
||||
<Setter Property="FontSize" Value="14"/>
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
<Setter Property="Cursor" Value="Hand"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Border Background="{TemplateBinding Background}"
|
||||
CornerRadius="4"
|
||||
Padding="{TemplateBinding Padding}">
|
||||
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Setter Property="OverridesDefaultStyle" Value="True"/>
|
||||
</Style>
|
||||
|
||||
<!-- TextBlock Style -->
|
||||
<Style TargetType="TextBlock">
|
||||
<Setter Property="Foreground" Value="White"/>
|
||||
<Setter Property="FontSize" Value="14"/>
|
||||
<Setter Property="VerticalAlignment" Value="Center"/>
|
||||
</Style>
|
||||
|
||||
</Window.Resources>
|
||||
|
||||
<Grid>
|
||||
<StackPanel>
|
||||
<TextBlock Text="Bitte folgende Schritte durchführen, um die Teams API zu aktivieren und damit die Anwendung zu genehmigen." Margin="10" Height="45" TextWrapping="Wrap" TextAlignment="Center" FontWeight="Bold" FontSize="16"/>
|
||||
|
||||
<StackPanel x:Name="Step1Panel" Margin="10">
|
||||
<TextBlock Text="1. Aktivieren der Teams Drittanbieter API:"/>
|
||||
<!-- Hyperlink in einem TextBlock unterbringen -->
|
||||
<TextBlock>
|
||||
<Hyperlink x:Name="Step1Link" NavigateUri="https://support.microsoft.com/de-de/office/herstellen-einer-verbindung-mit-drittanbieterger%C3%A4ten-in-microsoft-teams-aabca9f2-47bb-407f-9f9b-81a104a883d6" RequestNavigate="Step1Link_RequestNavigate">
|
||||
<Run Text="Anleitung für die Aktivierung der Drittanbieter API"/>
|
||||
</Hyperlink>
|
||||
</TextBlock>
|
||||
<Button x:Name="Step1Button" Content="Drittanbieter-API aktiviert" Width="200" Margin="10" Click="Step1Button_Click"/>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel x:Name="Step2Panel" Margin="10" Height="61" IsEnabled="False" >
|
||||
<StackPanel.Effect >
|
||||
<Wpf:TintEffect Tint="#FFA4A0A0"/>
|
||||
</StackPanel.Effect>
|
||||
<TextBlock Text="2. Starten Sie ein Teams Meeting mit sich selbst:"/>
|
||||
<Button x:Name="Step2Button" Content="Teams Meeting ist gestartet" Width="200" Margin="10" Click="Step2Button_Click"/>
|
||||
<TextBlock x:Name="Step2Status" Margin="10"/>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Schritt 3: Authentifizieren -->
|
||||
<StackPanel x:Name="Step3Panel" Margin="10" IsEnabled="False" >
|
||||
<StackPanel.Effect >
|
||||
<Wpf:TintEffect Tint="#FFA4A0A0"/>
|
||||
</StackPanel.Effect>
|
||||
<TextBlock Text="3. Zulassen der Verbindungsanforderung:"/>
|
||||
<TextBlock Margin="10,0,10,0" Height="34" TextWrapping="Wrap" TextAlignment="Center" FontSize="12"><Run Text="Nach dem Klick auf dem Button wird in Teams eine Anforderung sichtbar, diese bitte zulassen"/></TextBlock>
|
||||
<Button x:Name="Step3Button" Content="Verbindungsanforderung senden" Width="227" Margin="10" Click="Step3Button_Click"/>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Schritt 3: Authentifizieren -->
|
||||
<StackPanel x:Name="FinishPanel" Margin="10" IsEnabled="{Binding FinishPanelEnabled}" Effect="{Binding FinishPanelEffect}" >
|
||||
<TextBlock Foreground="{Binding FinishPanelFinishTextColor}" Text="{Binding FinishPanelFinishTextText}" FontWeight="Bold" HorizontalAlignment="Center"/>
|
||||
<Button x:Name="FinishButton" Content="Abschließen" Width="200" Margin="10" Click="FinishButton_Click"/>
|
||||
</StackPanel>
|
||||
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Window>
|
||||
@@ -0,0 +1,83 @@
|
||||
using Emoji.Wpf;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Security.Policy;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Navigation;
|
||||
using System.Windows.Shapes;
|
||||
using TeamsNetphoneLink.WPF.MVVM;
|
||||
|
||||
namespace TeamsNetphoneLink.WPF
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaktionslogik für SetupLocalTeamsAPI.xaml
|
||||
/// </summary>
|
||||
public partial class SetupLocalTeamsAPI : Window
|
||||
{
|
||||
private Teams.TeamsLocalAPI teamsLocalAPI;
|
||||
public FinishPanelViewModel finishPanelViewModel { get; }
|
||||
|
||||
public SetupLocalTeamsAPI(Syncronisation sync)
|
||||
{
|
||||
teamsLocalAPI = sync.TeamsEvents;
|
||||
finishPanelViewModel = sync.FinishPanelViewModel;
|
||||
DataContext = finishPanelViewModel;
|
||||
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void Step1Link_RequestNavigate(object sender, RequestNavigateEventArgs e)
|
||||
{
|
||||
// Öffnet den Link, um die Teams API zu aktivieren
|
||||
Process.Start(new ProcessStartInfo(e.Uri.ToString().Replace("&", "^&")) { UseShellExecute = true });
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
private void Step1Button_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// Schritt 1 abgeschlossen - Schalte auf den nächsten Schritt
|
||||
Step1Panel.IsEnabled = false;
|
||||
Step2Panel.IsEnabled = true;
|
||||
Step1Panel.Effect = new TintEffect { Tint = Colors.DarkGray };
|
||||
Step2Panel.Effect = null;
|
||||
}
|
||||
|
||||
// Schritt 2: Teams Meeting starten
|
||||
private void Step2Button_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// Öffne die URL, um ein Meeting zu starten (Hier muss der genaue Link oder die API-Integration erfolgen)
|
||||
Step2Panel.IsEnabled = false;
|
||||
Step3Panel.IsEnabled = true;
|
||||
Step2Panel.Effect = new TintEffect { Tint = Colors.DarkGray };
|
||||
Step3Panel.Effect = null;
|
||||
}
|
||||
|
||||
// Schritt 3: Authentifizierung durchführen
|
||||
private async void Step3Button_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// Simuliere den Authentifizierungsprozess
|
||||
Step3Button.Content = "Authentifizierung gesendet...";
|
||||
Step3Button.IsEnabled = false;
|
||||
|
||||
this.finishPanelViewModel.FinishPanelFinishTextText = "Authentifizierung läuft...";
|
||||
this.finishPanelViewModel.FinishPanelFinishTextColor = new SolidColorBrush(Colors.Orange);
|
||||
|
||||
teamsLocalAPI.SendDummyCommand();
|
||||
}
|
||||
|
||||
private void FinishButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
this.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
<Window x:Class="TeamsNetphoneLink.WPF.UpdateWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
Title="Update verfügbar" Height="514" Width="833"
|
||||
Background="#FF1E1E1E" FontFamily="Segoe UI"
|
||||
WindowStyle="ToolWindow" ResizeMode="NoResize"
|
||||
WindowStartupLocation="CenterScreen" Closing="Window_Closing">
|
||||
|
||||
<!-- Dark Mode Resources -->
|
||||
<Window.Resources>
|
||||
<!-- Button Style -->
|
||||
<Style TargetType="Button">
|
||||
<Setter Property="Background" Value="#FF0078D4"/>
|
||||
<Setter Property="Foreground" Value="White"/>
|
||||
<Setter Property="BorderBrush" Value="Transparent"/>
|
||||
<Setter Property="Padding" Value="10,5"/>
|
||||
<Setter Property="FontSize" Value="14"/>
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
<Setter Property="Cursor" Value="Hand"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Border Background="{TemplateBinding Background}"
|
||||
CornerRadius="4"
|
||||
Padding="{TemplateBinding Padding}">
|
||||
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Setter Property="OverridesDefaultStyle" Value="True"/>
|
||||
</Style>
|
||||
|
||||
<!-- Special Style for Markdown TextBox -->
|
||||
<Style x:Key="MarkdownTextBoxStyle" TargetType="TextBox" BasedOn="{StaticResource {x:Type TextBox}}">
|
||||
<Setter Property="FontFamily" Value="Consolas"/>
|
||||
<Setter Property="Foreground" Value="#FFD4D4D4"/>
|
||||
<Setter Property="Background" Value="#FF1E1E1E"/>
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
<Setter Property="IsReadOnly" Value="True"/>
|
||||
<Setter Property="TextWrapping" Value="Wrap"/>
|
||||
<Setter Property="VerticalScrollBarVisibility" Value="Auto"/>
|
||||
<Setter Property="HorizontalScrollBarVisibility" Value="Disabled"/>
|
||||
</Style>
|
||||
|
||||
<!-- TextBox Style -->
|
||||
<Style TargetType="TextBox">
|
||||
<Setter Property="Background" Value="#FF252526"/>
|
||||
<Setter Property="Foreground" Value="White"/>
|
||||
<Setter Property="BorderBrush" Value="#FF3E3E40"/>
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
<Setter Property="Padding" Value="5"/>
|
||||
<Setter Property="FontSize" Value="14"/>
|
||||
<Setter Property="VerticalContentAlignment" Value="Center"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="TextBox">
|
||||
<Border Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
CornerRadius="4">
|
||||
<ScrollViewer x:Name="PART_ContentHost" Margin="{TemplateBinding Padding}"/>
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<!-- ProgressBar Style -->
|
||||
<Style TargetType="ProgressBar">
|
||||
<Setter Property="Background" Value="#FF252526"/>
|
||||
<Setter Property="BorderBrush" Value="#FF3E3E40"/>
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
<Setter Property="Foreground" Value="#FF0078D4"/>
|
||||
<Setter Property="Height" Value="10"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="ProgressBar">
|
||||
<Grid x:Name="Root">
|
||||
<Border x:Name="PART_Track"
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
CornerRadius="5"/>
|
||||
<Border x:Name="PART_Indicator"
|
||||
CornerRadius="5"
|
||||
HorizontalAlignment="Left">
|
||||
<Border.Background>
|
||||
<LinearGradientBrush StartPoint="0,0.5" EndPoint="1,0.5">
|
||||
<GradientStop Color="#FF0078D4" Offset="0"/>
|
||||
<GradientStop Color="#FF00B4FF" Offset="1"/>
|
||||
</LinearGradientBrush>
|
||||
</Border.Background>
|
||||
</Border>
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<!-- TextBlock Style -->
|
||||
<Style TargetType="TextBlock">
|
||||
<Setter Property="Foreground" Value="White"/>
|
||||
<Setter Property="FontSize" Value="14"/>
|
||||
<Setter Property="VerticalAlignment" Value="Center"/>
|
||||
</Style>
|
||||
|
||||
<!-- Title TextBlock Style -->
|
||||
<Style x:Key="TitleTextStyle" TargetType="TextBlock" BasedOn="{StaticResource {x:Type TextBlock}}">
|
||||
<Setter Property="FontSize" Value="18"/>
|
||||
<Setter Property="FontWeight" Value="Bold"/>
|
||||
<Setter Property="Margin" Value="10,10,10,20"/>
|
||||
</Style>
|
||||
|
||||
<!-- Status TextBlock Style -->
|
||||
<Style x:Key="StatusTextStyle" TargetType="TextBlock" BasedOn="{StaticResource {x:Type TextBlock}}">
|
||||
<Setter Property="Margin" Value="10,0,10,5"/>
|
||||
<Setter Property="Foreground" Value="#FFD4D4D4"/>
|
||||
</Style>
|
||||
|
||||
</Window.Resources>
|
||||
|
||||
<Grid Margin="10">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- Title -->
|
||||
<TextBlock x:Name="TitleText"
|
||||
Text="Es ist ein neues Update verfügbar"
|
||||
Style="{StaticResource TitleTextStyle}"
|
||||
Grid.Row="0"/>
|
||||
|
||||
<!-- Markdown Release Notes -->
|
||||
<Border Grid.Row="1"
|
||||
Margin="10,0,10,10"
|
||||
Background="#FF252526"
|
||||
CornerRadius="4"
|
||||
|
||||
BorderBrush="#FF3E3E40"
|
||||
BorderThickness="1">
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled" Padding="5">
|
||||
<TextBox x:Name="ReleaseNotesTextBox"
|
||||
Style="{StaticResource MarkdownTextBoxStyle}"
|
||||
HorizontalContentAlignment="Left"
|
||||
VerticalContentAlignment="Top"
|
||||
Text="Lade Release Notes..."/>
|
||||
</ScrollViewer>
|
||||
</Border>
|
||||
|
||||
<!-- Progress Status -->
|
||||
<TextBlock x:Name="ProgressStatusText"
|
||||
Text=""
|
||||
Style="{StaticResource StatusTextStyle}"
|
||||
Grid.Row="2"/>
|
||||
|
||||
<!-- Progress Bar -->
|
||||
<ProgressBar x:Name="UpdateProgress"
|
||||
Margin="10,0,10,10"
|
||||
Grid.Row="3"/>
|
||||
|
||||
<!-- Buttons -->
|
||||
<StackPanel Grid.Row="4"
|
||||
HorizontalAlignment="Right"
|
||||
Orientation="Horizontal"
|
||||
Margin="0,10,0,0">
|
||||
<Button x:Name="CloseButton"
|
||||
Content="Schließen"
|
||||
Width="100"
|
||||
Margin="0,0,10,0"
|
||||
Click="CloseButton_Click"/>
|
||||
<Button x:Name="UpdateButton"
|
||||
Content="Update durchführen"
|
||||
Width="150"
|
||||
Background="Green"
|
||||
Click="UpdateButton_Click"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Window>
|
||||
@@ -0,0 +1,229 @@
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Text.Json;
|
||||
using System.Windows;
|
||||
|
||||
namespace TeamsNetphoneLink.WPF
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaktionslogik für ExceptionWindow.xaml
|
||||
/// </summary>
|
||||
public partial class UpdateWindow : Window
|
||||
{
|
||||
private bool _updateRunning = false;
|
||||
private string _currentVersion;
|
||||
private string _latestVersion;
|
||||
|
||||
|
||||
public UpdateWindow(string currentVersion, string latestVersion)
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
// Step 1: Check for updates
|
||||
_currentVersion = currentVersion;
|
||||
_latestVersion = latestVersion;
|
||||
|
||||
TitleText.Text = $"Es ist ein Update von {currentVersion} auf {latestVersion} verfügbar";
|
||||
|
||||
LoadReleaseNotesAsync();
|
||||
}
|
||||
|
||||
private async void LoadReleaseNotesAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
var notes = await GetReleaseNotes();
|
||||
Dispatcher.Invoke(() =>
|
||||
{
|
||||
ReleaseNotesTextBox.Text = notes;
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Dispatcher.Invoke(() =>
|
||||
{
|
||||
ReleaseNotesTextBox.Text = $"Failed to load release notes: {ex.Message}";
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Button zum Schließen
|
||||
private void CloseButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
this.Close();
|
||||
}
|
||||
|
||||
// Button zum Updaten
|
||||
private void UpdateButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
this._updateRunning = true;
|
||||
this.UpdateButton.IsEnabled = false;
|
||||
this.CloseButton.IsEnabled = false;
|
||||
_ = UpdateApplicationAsync();
|
||||
}
|
||||
|
||||
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
|
||||
{
|
||||
if (this._updateRunning)
|
||||
{
|
||||
e.Cancel = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void StartUpdater(string tempExtractPath)
|
||||
{
|
||||
string appPath = AppDomain.CurrentDomain.BaseDirectory;
|
||||
string updaterPathEXE = Path.Combine(appPath, "TeamsNetphoneLinkUpdater.exe");
|
||||
string updaterPathDLL = Path.Combine(appPath, "TeamsNetphoneLinkUpdater.dll");
|
||||
string newUpdaterPathEXE = Path.Combine(tempExtractPath, "TeamsNetphoneLinkUpdater.exe");
|
||||
string newUpdaterPathDLL = Path.Combine(tempExtractPath, "TeamsNetphoneLinkUpdater.dll");
|
||||
|
||||
//Replace the updater if it exists in the new release
|
||||
if (File.Exists(newUpdaterPathEXE))
|
||||
{
|
||||
File.Copy(newUpdaterPathEXE, updaterPathEXE, true);
|
||||
}
|
||||
if (File.Exists(newUpdaterPathDLL))
|
||||
{
|
||||
File.Copy(newUpdaterPathDLL, updaterPathDLL, true);
|
||||
}
|
||||
|
||||
System.Diagnostics.Process p = new System.Diagnostics.Process();
|
||||
p.StartInfo.FileName = updaterPathEXE;
|
||||
p.StartInfo.ArgumentList.Add(appPath);
|
||||
p.StartInfo.ArgumentList.Add(tempExtractPath);
|
||||
p.StartInfo.UseShellExecute = true; // This will start the process in a new shell
|
||||
p.Start();
|
||||
|
||||
// Exit the application
|
||||
Application.Current.Shutdown();
|
||||
}
|
||||
|
||||
private static void UnzipRelease(string zipPath, string extractPath)
|
||||
{
|
||||
ZipFile.ExtractToDirectory(zipPath, extractPath);
|
||||
}
|
||||
|
||||
private async Task UpdateApplicationAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
if (_latestVersion != _currentVersion)
|
||||
{
|
||||
UpdateProgress.Value = 0;
|
||||
|
||||
// Step 2: Download the new release
|
||||
string downloadUrl = $"https://git.jan.sx/krjan02/TeamsNetphoneLink/releases/download/latest/NetphoneTeamsLink{_latestVersion}.zip";
|
||||
var tempZipPath = Path.GetTempFileName();
|
||||
tempZipPath = tempZipPath + ".TEAMSNETPHONEUPDATE";
|
||||
Console.WriteLine(tempZipPath);
|
||||
await DownloadReleaseAsync(downloadUrl, tempZipPath);
|
||||
|
||||
// Step 3: Unzip the release
|
||||
var tempExtractPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString() + "TEAMSNETPHONEUPDATE");
|
||||
|
||||
Directory.CreateDirectory(tempExtractPath);
|
||||
UnzipRelease(tempZipPath, tempExtractPath);
|
||||
|
||||
File.Delete(tempZipPath);
|
||||
|
||||
// Step 4: Replace Updater.exe and run the updater
|
||||
StartUpdater(tempExtractPath);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Dispatcher.Invoke(() => ProgressStatusText.Text = $"Error: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<string> GetReleaseNotes()
|
||||
{
|
||||
string apiUrl = $"https://git.jan.sx/api/v1/repos/krjan02/TeamsNetphoneLink/releases/tags/{_latestVersion}";
|
||||
using (HttpClient client = new HttpClient())
|
||||
{
|
||||
// Send a GET request to the Gitea API
|
||||
HttpResponseMessage response = await client.GetAsync(apiUrl);
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
// Parse the JSON response
|
||||
string jsonResponse = await response.Content.ReadAsStringAsync();
|
||||
using (JsonDocument doc = JsonDocument.Parse(jsonResponse))
|
||||
{
|
||||
JsonElement root = doc.RootElement;
|
||||
JsonElement body = root.GetProperty("body");
|
||||
|
||||
return body.GetString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private async Task<long> GetFileSize()
|
||||
{
|
||||
string apiUrl = $"https://git.jan.sx/api/v1/repos/krjan02/TeamsNetphoneLink/releases/tags/{_latestVersion}";
|
||||
using (HttpClient client = new HttpClient())
|
||||
{
|
||||
// Send a GET request to the Gitea API
|
||||
HttpResponseMessage response = await client.GetAsync(apiUrl);
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
// Parse the JSON response
|
||||
string jsonResponse = await response.Content.ReadAsStringAsync();
|
||||
using (JsonDocument doc = JsonDocument.Parse(jsonResponse))
|
||||
{
|
||||
JsonElement root = doc.RootElement;
|
||||
JsonElement assets = root.GetProperty("assets");
|
||||
|
||||
// Iterate through the assets to find the file
|
||||
foreach (JsonElement asset in assets.EnumerateArray())
|
||||
{
|
||||
string name = asset.GetProperty("name").GetString();
|
||||
if (name == $"NetphoneTeamsLink{_latestVersion}.zip")
|
||||
{
|
||||
long size = asset.GetProperty("size").GetInt64();
|
||||
return size;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private async Task DownloadReleaseAsync(string downloadUrl, string destinationPath)
|
||||
{
|
||||
using (var client = new HttpClient())
|
||||
{
|
||||
var response = await client.GetAsync(downloadUrl, HttpCompletionOption.ResponseHeadersRead);
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
var totalBytes = await GetFileSize();
|
||||
var downloadedBytes = 0L;
|
||||
var buffer = new byte[8192];
|
||||
|
||||
using (var fileStream = new FileStream(destinationPath, FileMode.Create, FileAccess.Write, FileShare.None))
|
||||
using (var stream = await response.Content.ReadAsStreamAsync())
|
||||
{
|
||||
int bytesRead;
|
||||
while ((bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length)) > 0)
|
||||
{
|
||||
fileStream.Write(buffer, 0, bytesRead);
|
||||
downloadedBytes += bytesRead;
|
||||
// Update progress in the UI
|
||||
Dispatcher.Invoke(() =>
|
||||
{
|
||||
ProgressStatusText.Text = $"Update wird heruntergeladen... ({downloadedBytes/1000}kB/{totalBytes/1000}kB)";
|
||||
UpdateProgress.Value = (double)downloadedBytes / totalBytes * 100;
|
||||
//StatusText.Text = $"Downloading... {DownloadProgress.Value:F2}%";
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user