Add some error handling

pull/17/head
Alexey Golub 7 years ago
parent f2178a0445
commit 8afe9852fb

@ -12,13 +12,14 @@ namespace DiscordChatExporter
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default); ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
// Services // Services
SimpleIoc.Default.Register<IDataService, DataService>(); SimpleIoc.Default.Register<IDataService, DataService>(true);
SimpleIoc.Default.Register<IExportService, ExportService>(); SimpleIoc.Default.Register<IExportService, ExportService>(true);
SimpleIoc.Default.Register<ISettingsService, SettingsService>(); SimpleIoc.Default.Register<ISettingsService, SettingsService>(true);
// View models // View models
SimpleIoc.Default.Register<IMainViewModel, MainViewModel>(); SimpleIoc.Default.Register<IErrorViewModel, ErrorViewModel>(true);
SimpleIoc.Default.Register<ISettingsViewModel, SettingsViewModel>(); SimpleIoc.Default.Register<IMainViewModel, MainViewModel>(true);
SimpleIoc.Default.Register<ISettingsViewModel, SettingsViewModel>(true);
// Load settings // Load settings
ServiceLocator.Current.GetInstance<ISettingsService>().Load(); ServiceLocator.Current.GetInstance<ISettingsService>().Load();
@ -30,6 +31,7 @@ namespace DiscordChatExporter
ServiceLocator.Current.GetInstance<ISettingsService>().Save(); ServiceLocator.Current.GetInstance<ISettingsService>().Save();
} }
public IErrorViewModel ErrorViewModel => ServiceLocator.Current.GetInstance<IErrorViewModel>();
public IMainViewModel MainViewModel => ServiceLocator.Current.GetInstance<IMainViewModel>(); public IMainViewModel MainViewModel => ServiceLocator.Current.GetInstance<IMainViewModel>();
public ISettingsViewModel SettingsViewModel => ServiceLocator.Current.GetInstance<ISettingsViewModel>(); public ISettingsViewModel SettingsViewModel => ServiceLocator.Current.GetInstance<ISettingsViewModel>();
} }

@ -84,12 +84,19 @@
<Reference Include="PresentationFramework" /> <Reference Include="PresentationFramework" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Exceptions\UnathorizedException.cs" />
<Compile Include="Messages\ShowErrorMessage.cs" />
<Compile Include="Messages\ShowSettingsMessage.cs" /> <Compile Include="Messages\ShowSettingsMessage.cs" />
<Compile Include="Models\AttachmentType.cs" /> <Compile Include="Models\AttachmentType.cs" />
<Compile Include="Models\ChannelChatLog.cs" /> <Compile Include="Models\ChannelChatLog.cs" />
<Compile Include="Models\ChannelType.cs" /> <Compile Include="Models\ChannelType.cs" />
<Compile Include="ViewModels\ErrorViewModel.cs" />
<Compile Include="ViewModels\IErrorViewModel.cs" />
<Compile Include="ViewModels\ISettingsViewModel.cs" /> <Compile Include="ViewModels\ISettingsViewModel.cs" />
<Compile Include="ViewModels\SettingsViewModel.cs" /> <Compile Include="ViewModels\SettingsViewModel.cs" />
<Compile Include="Views\ErrorDialog.ammy.cs">
<DependentUpon>ErrorDialog.ammy</DependentUpon>
</Compile>
<Compile Include="Views\SettingsDialog.ammy.cs"> <Compile Include="Views\SettingsDialog.ammy.cs">
<DependentUpon>SettingsDialog.ammy</DependentUpon> <DependentUpon>SettingsDialog.ammy</DependentUpon>
</Compile> </Compile>
@ -98,6 +105,11 @@
<Generator>XamlIntelliSenseFileGenerator</Generator> <Generator>XamlIntelliSenseFileGenerator</Generator>
<DependentUpon>App.ammy</DependentUpon> <DependentUpon>App.ammy</DependentUpon>
</Page> </Page>
<Page Include="Views\ErrorDialog.g.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
<DependentUpon>ErrorDialog.ammy</DependentUpon>
</Page>
<Page Include="Views\MainWindow.g.xaml"> <Page Include="Views\MainWindow.g.xaml">
<SubType>Designer</SubType> <SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
@ -150,6 +162,7 @@
<None Include="packages.config"> <None Include="packages.config">
<SubType>Designer</SubType> <SubType>Designer</SubType>
</None> </None>
<None Include="Views\ErrorDialog.ammy" />
<None Include="Views\MainWindow.ammy" /> <None Include="Views\MainWindow.ammy" />
<None Include="Views\SettingsDialog.ammy" /> <None Include="Views\SettingsDialog.ammy" />
</ItemGroup> </ItemGroup>

@ -0,0 +1,8 @@
using System;
namespace DiscordChatExporter.Exceptions
{
public class UnathorizedException : Exception
{
}
}

@ -0,0 +1,12 @@
namespace DiscordChatExporter.Messages
{
public class ShowErrorMessage
{
public string Message { get; }
public ShowErrorMessage(string message)
{
Message = message;
}
}
}

@ -1,8 +1,10 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Net;
using System.Net.Http; using System.Net.Http;
using System.Threading.Tasks; using System.Threading.Tasks;
using DiscordChatExporter.Exceptions;
using DiscordChatExporter.Models; using DiscordChatExporter.Models;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using Tyrrrz.Extensions; using Tyrrrz.Extensions;
@ -14,16 +16,30 @@ namespace DiscordChatExporter.Services
private const string ApiRoot = "https://discordapp.com/api/v6"; private const string ApiRoot = "https://discordapp.com/api/v6";
private readonly HttpClient _httpClient = new HttpClient(); private readonly HttpClient _httpClient = new HttpClient();
private async Task<string> GetStringAsync(string url)
{
using (var response = await _httpClient.GetAsync(url))
{
// Check status code
if (response.StatusCode.IsEither(HttpStatusCode.Unauthorized, HttpStatusCode.Forbidden))
throw new UnathorizedException();
response.EnsureSuccessStatusCode();
// Get content
return await response.Content.ReadAsStringAsync();
}
}
public async Task<IEnumerable<Guild>> GetGuildsAsync(string token) public async Task<IEnumerable<Guild>> GetGuildsAsync(string token)
{ {
// Form request url // Form request url
var url = $"{ApiRoot}/users/@me/guilds?token={token}&limit=100"; var url = $"{ApiRoot}/users/@me/guilds?token={token}&limit=100";
// Get response // Get response
var response = await _httpClient.GetStringAsync(url); var content = await GetStringAsync(url);
// Parse // Parse
var guilds = JArray.Parse(response).Select(ParseGuild); var guilds = JArray.Parse(content).Select(ParseGuild);
return guilds; return guilds;
} }
@ -34,10 +50,10 @@ namespace DiscordChatExporter.Services
var url = $"{ApiRoot}/users/@me/channels?token={token}"; var url = $"{ApiRoot}/users/@me/channels?token={token}";
// Get response // Get response
var response = await _httpClient.GetStringAsync(url); var content = await GetStringAsync(url);
// Parse // Parse
var channels = JArray.Parse(response).Select(ParseChannel); var channels = JArray.Parse(content).Select(ParseChannel);
return channels; return channels;
} }
@ -48,10 +64,10 @@ namespace DiscordChatExporter.Services
var url = $"{ApiRoot}/guilds/{guildId}/channels?token={token}"; var url = $"{ApiRoot}/guilds/{guildId}/channels?token={token}";
// Get response // Get response
var response = await _httpClient.GetStringAsync(url); var content = await GetStringAsync(url);
// Parse // Parse
var channels = JArray.Parse(response).Select(ParseChannel); var channels = JArray.Parse(content).Select(ParseChannel);
return channels; return channels;
} }
@ -71,10 +87,10 @@ namespace DiscordChatExporter.Services
url += $"&before={beforeId}"; url += $"&before={beforeId}";
// Get response // Get response
var response = await _httpClient.GetStringAsync(url); var content = await GetStringAsync(url);
// Parse // Parse
var messages = JArray.Parse(response).Select(ParseMessage); var messages = JArray.Parse(content).Select(ParseMessage);
// Add messages to list // Add messages to list
string currentMessageId = null; string currentMessageId = null;

@ -0,0 +1,15 @@
using DiscordChatExporter.Messages;
using GalaSoft.MvvmLight;
namespace DiscordChatExporter.ViewModels
{
public class ErrorViewModel : ViewModelBase, IErrorViewModel
{
public string Message { get; private set; }
public ErrorViewModel()
{
MessengerInstance.Register<ShowErrorMessage>(this, m => Message = m.Message);
}
}
}

@ -0,0 +1,7 @@
namespace DiscordChatExporter.ViewModels
{
public interface IErrorViewModel
{
string Message { get; }
}
}

@ -2,6 +2,7 @@
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using DiscordChatExporter.Exceptions;
using DiscordChatExporter.Messages; using DiscordChatExporter.Messages;
using DiscordChatExporter.Models; using DiscordChatExporter.Models;
using DiscordChatExporter.Services; using DiscordChatExporter.Services;
@ -107,6 +108,8 @@ namespace DiscordChatExporter.ViewModels
// Clear existing // Clear existing
_guildChannelsMap.Clear(); _guildChannelsMap.Clear();
try
{
// Get DM channels // Get DM channels
{ {
var channels = await _dataService.GetDirectMessageChannelsAsync(_cachedToken); var channels = await _dataService.GetDirectMessageChannelsAsync(_cachedToken);
@ -124,6 +127,11 @@ namespace DiscordChatExporter.ViewModels
_guildChannelsMap[guild] = channels.ToArray(); _guildChannelsMap[guild] = channels.ToArray();
} }
} }
}
catch (UnathorizedException)
{
MessengerInstance.Send(new ShowErrorMessage("Failed to authorize. Make sure the token is valid."));
}
AvailableGuilds = _guildChannelsMap.Keys.ToArray(); AvailableGuilds = _guildChannelsMap.Keys.ToArray();
SelectedGuild = AvailableGuilds.FirstOrDefault(); SelectedGuild = AvailableGuilds.FirstOrDefault();
@ -135,13 +143,13 @@ namespace DiscordChatExporter.ViewModels
IsBusy = true; IsBusy = true;
// Get safe file names // Get safe file names
var safeGroupName = SelectedGuild.Name.Replace(Path.GetInvalidFileNameChars(), '_'); var safeGuildName = SelectedGuild.Name.Replace(Path.GetInvalidFileNameChars(), '_');
var safeChannelName = channel.Name.Replace(Path.GetInvalidFileNameChars(), '_'); var safeChannelName = channel.Name.Replace(Path.GetInvalidFileNameChars(), '_');
// Ask for path // Ask for path
var sfd = new SaveFileDialog var sfd = new SaveFileDialog
{ {
FileName = $"{safeGroupName} - {safeChannelName}.html", FileName = $"{safeGuildName} - {safeChannelName}.html",
Filter = "HTML files (*.html)|*.html|All files (*.*)|*.*", Filter = "HTML files (*.html)|*.html|All files (*.*)|*.*",
DefaultExt = "html", DefaultExt = "html",
AddExtension = true AddExtension = true
@ -152,6 +160,9 @@ namespace DiscordChatExporter.ViewModels
return; return;
} }
// Export
try
{
// Get messages // Get messages
var messages = await _dataService.GetChannelMessagesAsync(_cachedToken, channel.Id); var messages = await _dataService.GetChannelMessagesAsync(_cachedToken, channel.Id);
@ -160,6 +171,11 @@ namespace DiscordChatExporter.ViewModels
// Export // Export
_exportService.Export(sfd.FileName, chatLog, _settingsService.Theme); _exportService.Export(sfd.FileName, chatLog, _settingsService.Theme);
}
catch (UnathorizedException)
{
MessengerInstance.Send(new ShowErrorMessage("Failed to export. You don't have access to that channel."));
}
IsBusy = false; IsBusy = false;
} }

@ -0,0 +1,25 @@
using MaterialDesignThemes.Wpf
UserControl "DiscordChatExporter.Views.ErrorDialog" {
DataContext: bind ErrorViewModel from $resource Container
Width: 250
StackPanel {
// Message
TextBlock {
Margin: 8
FontSize: 16
HorizontalAlignment: Center
TextWrapping: WrapWithOverflow
Text: bind Message
}
// OK
Button {
Command: DialogHost.CloseDialogCommand
Content: "OK"
Margin: 8
Style: resource dyn "MaterialDesignFlatButton"
}
}
}

@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DiscordChatExporter.Views
{
public partial class ErrorDialog
{
public ErrorDialog()
{
InitializeComponent();
}
}
}

@ -17,6 +17,7 @@ namespace DiscordChatExporter.Views
InitializeComponent(); InitializeComponent();
Title += $" v{Assembly.GetExecutingAssembly().GetName().Version}"; Title += $" v{Assembly.GetExecutingAssembly().GetName().Version}";
Messenger.Default.Register<ShowErrorMessage>(this, m => DialogHost.Show(new ErrorDialog()).Forget());
Messenger.Default.Register<ShowSettingsMessage>(this, m => DialogHost.Show(new SettingsDialog()).Forget()); Messenger.Default.Register<ShowSettingsMessage>(this, m => DialogHost.Show(new SettingsDialog()).Forget());
} }

Loading…
Cancel
Save