You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

334 lines
11 KiB

2 months ago
using System;
2 months ago
using System.IO;
2 months ago
using System.Windows;
2 months ago
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Threading;
using Housing.Services;
2 months ago
namespace Housing.Login;
public sealed class StartupLoginWindowResult
{
public string Maker { get; set; } = string.Empty;
public string Model { get; set; } = string.Empty;
public string Variant1 { get; set; } = string.Empty;
public string Variant2 { get; set; } = string.Empty;
public string Operator { get; set; } = string.Empty;
public string Password { get; set; } = string.Empty;
public string LineNo { get; set; } = string.Empty;
public string LotNo { get; set; } = string.Empty;
public string JigNo { get; set; } = string.Empty;
}
public partial class StartupLoginWindow : Window
{
2 months ago
private const string OperatorHistoryFieldName = "Operator";
private const string SuppressedOperatorHistoryValue = "su";
private readonly LoginHistoryStore _loginHistoryStore;
private Popup? _openHistoryPopup;
2 months ago
public StartupLoginWindowResult Result { get; private set; } = new();
public StartupLoginWindow()
{
2 months ago
_loginHistoryStore = LoginHistoryStore.Load(GetLoginHistoryPath());
RemoveSuppressedOperatorHistory();
2 months ago
InitializeComponent();
2 months ago
AttachLoginHistoryMenus();
PasswordBox.GotKeyboardFocus += (_, _) => CloseHistoryPopup();
LoginButton.GotKeyboardFocus += (_, _) => CloseHistoryPopup();
2 months ago
OperatorTextBox.Focus();
}
2 months ago
private async void LogInButton_Click(object sender, RoutedEventArgs e)
2 months ago
{
2 months ago
CloseHistoryPopup();
2 months ago
2 months ago
if (!TryReadRequiredFields(out var loginResult))
2 months ago
{
return;
}
2 months ago
if (sender is Button button)
2 months ago
{
2 months ago
button.IsEnabled = false;
2 months ago
}
2 months ago
try
2 months ago
{
2 months ago
var loginSettings = LoginSettings.Load(GetDatabaseIniPath());
if (loginSettings.OfflinePreview)
{
Result = loginResult;
SaveLoginHistory(loginResult);
DialogResult = true;
Close();
return;
}
var repository = new LoginAccountRepository(
DatabaseSettings.Load(GetDatabaseIniPath()),
loginSettings);
if (!await repository.ExistsAsync(loginResult.Operator, loginResult.Password))
{
MessageBox.Show(this, "DB에 등록된 ID/PW가 아닙니다.", "Login", MessageBoxButton.OK, MessageBoxImage.Error);
PasswordBox.Clear();
PasswordBox.Focus();
return;
}
Result = loginResult;
SaveLoginHistory(loginResult);
DialogResult = true;
Close();
2 months ago
}
2 months ago
catch (Exception ex)
{
MessageBox.Show(this, $"로그인 DB 확인 실패: {ex.Message}", "Login", MessageBoxButton.OK, MessageBoxImage.Error);
OperatorTextBox.Focus();
OperatorTextBox.SelectAll();
}
finally
{
if (sender is Button loginButton)
{
loginButton.IsEnabled = true;
}
}
}
2 months ago
2 months ago
private bool TryReadRequiredFields(out StartupLoginWindowResult result)
{
result = new StartupLoginWindowResult();
if (!TryReadTextBox(MakerTextBox, "Maker", out var maker) ||
!TryReadTextBox(ModelTextBox, "Model", out var model) ||
!TryReadTextBox(Variant1TextBox, "Variant 1", out var variant1) ||
!TryReadTextBox(Variant2TextBox, "Variant 2", out var variant2) ||
!TryReadTextBox(OperatorTextBox, "ID", out var loginId) ||
!TryReadPasswordBox(PasswordBox, "PW", out var password) ||
!TryReadTextBox(LineNoTextBox, "Line", out var lineNo) ||
!TryReadTextBox(LotNoTextBox, "Lot No", out var lotNo) ||
!TryReadTextBox(JigNoTextBox, "Jig No", out var jigNo))
{
return false;
}
result = new StartupLoginWindowResult
2 months ago
{
2 months ago
Maker = maker,
Model = model,
Variant1 = variant1,
Variant2 = variant2,
2 months ago
Operator = loginId,
Password = password,
2 months ago
LineNo = lineNo,
LotNo = lotNo,
JigNo = jigNo
2 months ago
};
2 months ago
return true;
}
private void AttachLoginHistoryMenus()
{
AttachHistoryMenu(MakerTextBox, "Maker", ModelTextBox);
AttachHistoryMenu(ModelTextBox, "Model", Variant1TextBox);
AttachHistoryMenu(Variant1TextBox, "Variant1", Variant2TextBox);
AttachHistoryMenu(Variant2TextBox, "Variant2", OperatorTextBox);
AttachHistoryMenu(OperatorTextBox, OperatorHistoryFieldName, PasswordBox);
AttachHistoryMenu(LineNoTextBox, "LineNo", LotNoTextBox);
AttachHistoryMenu(LotNoTextBox, "LotNo", JigNoTextBox);
AttachHistoryMenu(JigNoTextBox, "JigNo", LoginButton);
}
private void AttachHistoryMenu(TextBox textBox, string fieldName, Control nextFocusControl)
{
textBox.GotKeyboardFocus += (_, _) => ShowHistoryMenu(textBox, fieldName, nextFocusControl);
textBox.PreviewMouseLeftButtonDown += (_, _) =>
{
if (textBox.IsKeyboardFocusWithin)
{
ShowHistoryMenu(textBox, fieldName, nextFocusControl);
}
};
}
private void ShowHistoryMenu(TextBox textBox, string fieldName, Control nextFocusControl)
{
var values = _loginHistoryStore.GetValues(fieldName);
if (values.Count == 0)
{
CloseHistoryPopup();
return;
}
CloseHistoryPopup();
var menuWidth = Math.Max(160, textBox.ActualWidth);
var stackPanel = new StackPanel();
var popup = new Popup
{
PlacementTarget = textBox,
Placement = PlacementMode.Bottom,
HorizontalOffset = 0,
StaysOpen = true,
AllowsTransparency = true,
Focusable = false
};
foreach (var value in values)
{
var item = new Button
{
Content = value,
MinWidth = menuWidth,
Padding = new Thickness(10, 6, 10, 6),
BorderThickness = new Thickness(0),
Background = textBox.Background,
Foreground = textBox.Foreground,
HorizontalContentAlignment = HorizontalAlignment.Left
};
item.Click += (_, _) =>
{
textBox.Text = value;
textBox.CaretIndex = textBox.Text.Length;
CloseHistoryPopup();
FocusControl(nextFocusControl);
};
stackPanel.Children.Add(item);
}
popup.Child = new Border
{
Width = menuWidth,
MaxHeight = 180,
Background = textBox.Background,
BorderBrush = textBox.BorderBrush,
BorderThickness = new Thickness(1),
Child = new ScrollViewer
{
MaxHeight = 180,
VerticalScrollBarVisibility = ScrollBarVisibility.Auto,
Content = stackPanel
}
};
_openHistoryPopup = popup;
popup.IsOpen = true;
}
private void CloseHistoryPopup()
{
if (_openHistoryPopup is not null)
{
_openHistoryPopup.IsOpen = false;
_openHistoryPopup = null;
}
}
private void FocusControl(Control control)
{
Dispatcher.BeginInvoke(new Action(() =>
{
control.Focus();
if (control is TextBox textBox)
{
textBox.CaretIndex = textBox.Text.Length;
}
else if (control is PasswordBox passwordBox)
{
passwordBox.Focus();
}
}), DispatcherPriority.Input);
}
private void SaveLoginHistory(StartupLoginWindowResult result)
{
_loginHistoryStore.Remember("Maker", result.Maker);
_loginHistoryStore.Remember("Model", result.Model);
_loginHistoryStore.Remember("Variant1", result.Variant1);
_loginHistoryStore.Remember("Variant2", result.Variant2);
RememberOperatorHistory(result.Operator);
_loginHistoryStore.Remember("LineNo", result.LineNo);
_loginHistoryStore.Remember("LotNo", result.LotNo);
_loginHistoryStore.Remember("JigNo", result.JigNo);
_loginHistoryStore.Save();
}
private void RememberOperatorHistory(string operatorId)
{
if (IsSuppressedOperatorHistoryValue(operatorId))
{
_loginHistoryStore.Forget(OperatorHistoryFieldName, operatorId);
return;
}
_loginHistoryStore.Remember(OperatorHistoryFieldName, operatorId);
}
private void RemoveSuppressedOperatorHistory()
{
try
{
if (_loginHistoryStore.Forget(OperatorHistoryFieldName, SuppressedOperatorHistoryValue))
{
_loginHistoryStore.Save();
}
}
catch
{
// Login history cleanup is optional and should not block the login window.
}
}
private static bool IsSuppressedOperatorHistoryValue(string value)
{
return string.Equals(value.Trim(), SuppressedOperatorHistoryValue, StringComparison.OrdinalIgnoreCase);
}
private bool TryReadTextBox(TextBox textBox, string fieldName, out string value)
{
value = textBox.Text.Trim();
if (!string.IsNullOrWhiteSpace(value))
{
return true;
}
MessageBox.Show(this, $"{fieldName}을(를) 입력하세요.", "Login", MessageBoxButton.OK, MessageBoxImage.Warning);
textBox.Focus();
textBox.SelectAll();
return false;
}
private bool TryReadPasswordBox(PasswordBox passwordBox, string fieldName, out string value)
{
value = passwordBox.Password;
if (!string.IsNullOrWhiteSpace(value))
{
return true;
}
MessageBox.Show(this, $"{fieldName}을(를) 입력하세요.", "Login", MessageBoxButton.OK, MessageBoxImage.Warning);
passwordBox.Focus();
passwordBox.SelectAll();
return false;
}
private static string GetDatabaseIniPath()
{
return Path.Combine(AppContext.BaseDirectory, "Database.ini");
}
private static string GetLoginHistoryPath()
{
return Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
"Housing",
"LoginHistory.json");
2 months ago
}
}