Replace explicit types with explicit types

This commit is contained in:
Данил Ярош 2026-01-13 08:44:14 +03:00
parent 674e470272
commit e261cc77de
14 changed files with 157 additions and 176 deletions

View File

@ -1,4 +1,4 @@
using CORP_MAUI.Pages;
using CORP_MAUI.Pages;
using CORP_MAUI.Models;
using System.Diagnostics;
@ -29,7 +29,7 @@ public partial class App : Application
// Создаем окно с начальной страницей загрузки
#if ANDROID || IOS || MACCATALYST || WINDOWS
var loadingPage = new ContentPage
ContentPage loadingPage = new ContentPage
{
Content = new VerticalStackLayout
{
@ -52,10 +52,10 @@ public partial class App : Application
}
};
var window = new Window(loadingPage);
Window window = new Window(loadingPage);
#else
// Фолбэк для платформ вне целевых (не должен использоваться в текущих TFM)
var window = new Window();
Window window = new Window();
#endif
// Асинхронно проверяем токен и определяем начальную страницу
@ -114,7 +114,7 @@ public partial class App : Application
try
{
Trace.WriteLine("[App] >>> Connecting to WebSocket...");
var token = ServiceProvider.ApiService.GetAccessToken();
string? token = ServiceProvider.ApiService.GetAccessToken();
if (!string.IsNullOrEmpty(token))
{

View File

@ -44,7 +44,7 @@ public partial class AppShell : Shell
await Task.Delay(100);
await MainThread.InvokeOnMainThreadAsync(async () =>
{
var mainPage = Application.Current?.Windows[0]?.Page;
Page? mainPage = Application.Current?.Windows[0]?.Page;
if (mainPage != null)
{
await mainPage.DisplayAlert("Ошибка AppShell",
@ -72,7 +72,7 @@ public partial class AppShell : Shell
if (ServiceProvider.ApiService.IsAuthenticated)
{
// Получаем токен для WebSocket
var accessToken = await SecureStorage.GetAsync("api.jwt_access");
string? accessToken = await SecureStorage.GetAsync("api.jwt_access");
Trace.WriteLine($"[AppShell] Access token exists: {!string.IsNullOrEmpty(accessToken)}");
Trace.WriteLine($"[AppShell] WebSocket connected: {ServiceProvider.WebSocketService.IsConnected}");

View File

@ -1,4 +1,4 @@
using System.Diagnostics;
using System.Diagnostics;
using CORP_MAUI.Services;
using CORP_MAUI.Pages;
@ -8,7 +8,7 @@ public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
MauiAppBuilder builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.ConfigureFonts(fonts =>

View File

@ -47,11 +47,8 @@ public class Comment
get
{
// Если CreatedAt валидный (больше 2000 года, чтоб отсечь дефолт), берем его
if (CreatedAt.Year > 2000) return CreatedAt;
// Иначе пробуем поле Date
if (Date.HasValue) return Date.Value;
// Иначе текущее время
return DateTime.Now;
DateTime d = CreatedAt.Year > 2000 ? CreatedAt : (Date ?? DateTime.Now);
return d.ToLocalTime();
}
}

View File

@ -34,7 +34,7 @@ public class Post
{
get
{
var url = !string.IsNullOrEmpty(ImageUrl) ? ImageUrl : Image;
string? url = !string.IsNullOrEmpty(ImageUrl) ? ImageUrl : Image;
if (string.IsNullOrEmpty(url)) return null;
// Если ссылка уже абсолютная - возвращаем как есть
@ -42,7 +42,7 @@ public class Post
// Если ссылка относительная - добавляем домен
// Обрабатываем случай, когда url начинается со / или без
var cleanUrl = url.TrimStart('/');
string cleanUrl = url.TrimStart('/');
return $"https://corp.robotail.pro/{cleanUrl}";
}
}
@ -51,7 +51,7 @@ public class Post
public DateTime? Date { get; set; }
[JsonIgnore]
public DateTime DisplayDate => CreatedAt.Year > 2000 ? CreatedAt : (Date ?? DateTime.Now);
public DateTime DisplayDate => (CreatedAt.Year > 2000 ? CreatedAt : (Date ?? DateTime.Now)).ToLocalTime();
public User? Author { get; set; }

View File

@ -16,7 +16,7 @@ public partial class ChatPage : ContentPage
InitializeComponent();
_recipient = recipient;
_messages = new ObservableCollection<MessageViewModel>();
_messages = [];
MessagesCollectionView.ItemsSource = _messages;
@ -34,7 +34,7 @@ public partial class ChatPage : ContentPage
try
{
// Получаем текущего пользователя
var user = await ServiceProvider.ApiService.GetCurrentUserAsync();
User? user = await ServiceProvider.ApiService.GetCurrentUserAsync();
if (user != null)
{
_currentUser = user;
@ -63,14 +63,14 @@ public partial class ChatPage : ContentPage
private async void OnSendMessageClicked(object sender, EventArgs e)
{
var content = MessageEntry.Text?.Trim();
string? content = MessageEntry.Text?.Trim();
if (string.IsNullOrEmpty(content))
return;
try
{
var message = new Message
Message message = new Message
{
SenderId = _currentUser.Id,
SenderName = _currentUser.FullName ?? _currentUser.Username,
@ -162,7 +162,7 @@ public partial class ChatPage : ContentPage
{
_messages.Clear();
foreach (var message in history.OrderBy(m => m.Timestamp))
foreach (Message message in history.OrderBy(m => m.Timestamp))
{
_messages.Add(new MessageViewModel
{

View File

@ -1,4 +1,5 @@
using System.Diagnostics;
using CORP_MAUI.Models;
namespace CORP_MAUI.Pages;
@ -14,8 +15,8 @@ public partial class LoginPage : ContentPage
{
Trace.WriteLine("=== LOGIN BUTTON CLICKED ===");
var username = UsernameEntry.Text?.Trim();
var password = PasswordEntry.Text?.Trim();
string? username = UsernameEntry.Text?.Trim();
string? password = PasswordEntry.Text?.Trim();
Trace.WriteLine($"[LoginPage] >>> Кнопка входа нажата");
Trace.WriteLine($"[LoginPage] Username: {username}");
@ -36,7 +37,7 @@ public partial class LoginPage : ContentPage
Trace.WriteLine($"[LoginPage] >>> Вызываем ApiService.LoginAsync...");
// Выполняем вход
var result = await ServiceProvider.ApiService.LoginAsync(username, password);
AuthResponse result = await ServiceProvider.ApiService.LoginAsync(username, password);
Trace.WriteLine($"[LoginPage] <<< Получен результат логина");
Trace.WriteLine($"[LoginPage] Success: {result.Success}");
@ -50,7 +51,7 @@ public partial class LoginPage : ContentPage
try
{
// Получаем токен (из результата или напрямую из ApiService)
var token = result.Token ?? ServiceProvider.ApiService.GetAccessToken();
string? token = result.Token ?? ServiceProvider.ApiService.GetAccessToken();
if (string.IsNullOrEmpty(token))
{
@ -99,7 +100,7 @@ public partial class LoginPage : ContentPage
await MainThread.InvokeOnMainThreadAsync(() =>
{
Trace.WriteLine($"[LoginPage] >>> Создание AppShell...");
var app = Application.Current;
Application? app = Application.Current;
if (app != null && app.Windows.Count > 0)
{
app.Windows[0].Page = new AppShell();

View File

@ -1,4 +1,5 @@
using System.Diagnostics;
using CORP_MAUI.Models;
using CORP_MAUI.ViewModels;
namespace CORP_MAUI.Pages;
@ -28,12 +29,12 @@ public partial class PostDetailsPage : ContentPage
_viewModel.IsLoadingComments = true;
try
{
var response = await ServiceProvider.ApiService.GetCommentsPaginatedAsync(postId: _viewModel.PostViewModel.Post.Id);
PaginatedResponse<Comment> response = await ServiceProvider.ApiService.GetCommentsPaginatedAsync(postId: _viewModel.PostViewModel.Post.Id);
_viewModel.Comments.Clear();
if (response.Results != null)
{
foreach (var comment in response.Results)
foreach (Comment comment in response.Results)
{
_viewModel.Comments.Add(comment);
}

View File

@ -1,5 +1,6 @@
using System.Collections.ObjectModel;
using System.Diagnostics;
using CORP_MAUI.Models;
using CORP_MAUI.ViewModels;
namespace CORP_MAUI.Pages;
@ -43,7 +44,7 @@ public partial class PostsPage
LoadingIndicator.IsVisible = true;
LoadingIndicator.IsRunning = true;
var response = await ServiceProvider.ApiService.GetPostsPaginatedAsync(_currentPage);
PaginatedResponse<Post> response = await ServiceProvider.ApiService.GetPostsPaginatedAsync(_currentPage);
if (refresh)
{
@ -52,85 +53,66 @@ public partial class PostsPage
if (response.Results != null)
{
foreach (var post in response.Results)
IEnumerable<Task<PostViewModel>> tasks = response.Results.Select(async post =>
{
Trace.WriteLine($"[PostsPage] Post {post.Id}: '{post.Title}'");
Trace.WriteLine($" CreatedAt: {post.CreatedAt}");
Trace.WriteLine($" DisplayImageUrl: {post.DisplayImageUrl}");
Trace.WriteLine($" Content length: {post.Content?.Length ?? 0}");
PostViewModel vm = new PostViewModel(post);
var vm = new PostViewModel(post);
_posts.Add(vm);
// Асинхронно загружаем изображение с токеном
_ = Task.Run(async () =>
if (!string.IsNullOrEmpty(post.DisplayImageUrl))
{
if (!string.IsNullOrEmpty(post.DisplayImageUrl))
try
{
try
Uri uri = new Uri(post.DisplayImageUrl);
// Sanitize filename
string fileName = Path.GetFileName(uri.LocalPath);
foreach (char invalidChar in Path.GetInvalidFileNameChars())
{
// Используем имя файла из URL как ключ кэша
var uri = new Uri(post.DisplayImageUrl);
var fileName = Path.GetFileName(uri.LocalPath);
// Добавляем префикс, чтобы избежать коллизий
var cacheFileName = $"cache_{post.Id}_{fileName}";
var cacheFilePath = Path.Combine(FileSystem.CacheDirectory, cacheFileName);
// Проверяем кэш
if (File.Exists(cacheFilePath))
{
var fileInfo = new FileInfo(cacheFilePath);
if (fileInfo.Length > 0)
{
Trace.WriteLine($"[PostsPage] >> Image found in cache: {cacheFilePath}");
await MainThread.InvokeOnMainThreadAsync(() =>
{
vm.LocalImagePath = cacheFilePath;
});
return;
}
else
{
// Удаляем пустой файл
File.Delete(cacheFilePath);
}
}
fileName = fileName.Replace(invalidChar, '_');
}
// Загружаем, если нет в кэше
var bytes = await ServiceProvider.ApiService.GetImageBytesAsync(post.DisplayImageUrl);
string cacheFileName = $"post_img_{post.Id}_{fileName}";
string cacheFilePath = Path.Combine(FileSystem.CacheDirectory, cacheFileName);
bool isCached = false;
// 1. Проверяем кэш
if (File.Exists(cacheFilePath))
{
FileInfo fileInfo = new FileInfo(cacheFilePath);
if (fileInfo.Length > 0)
{
Trace.WriteLine($"[PostsPage] Изображение из кэша: {cacheFilePath}");
vm.LocalImagePath = cacheFilePath;
isCached = true;
}
}
// 2. Если нет в кэше - загружаем
if (!isCached)
{
Trace.WriteLine($"[PostsPage] Загрузка изображения: {post.DisplayImageUrl}");
byte[]? bytes = await ServiceProvider.ApiService.GetImageBytesAsync(post.DisplayImageUrl);
if (bytes != null && bytes.Length > 0)
{
Trace.WriteLine($"[PostsPage] >> Image loaded ({bytes.Length} bytes), saving to {cacheFilePath}");
// Сразу показываем
await MainThread.InvokeOnMainThreadAsync(() =>
{
vm.AuthorizedImageSource = ImageSource.FromStream(() => new MemoryStream(bytes));
});
await File.WriteAllBytesAsync(cacheFilePath, bytes);
// Обновляем путь (на всякий случай, для будущего)
await MainThread.InvokeOnMainThreadAsync(() =>
{
vm.LocalImagePath = cacheFilePath;
});
Trace.WriteLine($"[PostsPage] Сохранено в кэш: {cacheFilePath}");
vm.LocalImagePath = cacheFilePath;
}
else
{
Trace.WriteLine($"[PostsPage] >> Image failed to load for post {post.Id} (null/empty)");
}
}
catch(Exception imgEx)
{
Trace.WriteLine($"[PostsPage] >> Exception loading image for post {post.Id}: {imgEx.Message}");
}
}
else
catch(Exception imgEx)
{
Trace.WriteLine($"[PostsPage] >> No image URL for post {post.Id}");
Trace.WriteLine($"[PostsPage] Ошибка обработки изображения для поста {post.Id}: {imgEx.Message}");
}
});
}
return vm;
});
// Ждем загрузки всех изображений перед отображением списка
PostViewModel[] viewModels = await Task.WhenAll(tasks);
foreach (PostViewModel vm in viewModels)
{
_posts.Add(vm);
}
}
}

View File

@ -20,7 +20,7 @@ public partial class ProfilePage : ContentPage
LoadingIndicator.IsRunning = true;
// Загружаем профиль
var user = await ServiceProvider.ApiService.GetCurrentUserAsync();
User? user = await ServiceProvider.ApiService.GetCurrentUserAsync();
if (user != null)
{
@ -57,7 +57,7 @@ public partial class ProfilePage : ContentPage
private async void OnLogoutClicked(object sender, EventArgs e)
{
var result = await DisplayAlert("Выход", "Вы уверены, что хотите выйти?", "Да", "Нет");
bool result = await DisplayAlert("Выход", "Вы уверены, что хотите выйти?", "Да", "Нет");
if (result)
{

View File

@ -45,7 +45,7 @@ public partial class UsersPage : ContentPage
Task.Run(async () =>
{
await Task.Delay(100);
var mainPage = Application.Current?.Windows[0]?.Page;
Page? mainPage = Application.Current?.Windows[0]?.Page;
if (mainPage != null)
{
await mainPage.DisplayAlert("Ошибка UsersPage",
@ -71,7 +71,7 @@ public partial class UsersPage : ContentPage
Trace.WriteLine($"[UsersPage] >>> Получено пользователей: {_allUsers.Count}");
_users.Clear();
foreach (var user in _allUsers.OrderBy(u => u.FullName))
foreach (User user in _allUsers.OrderBy(u => u.FullName))
{
_users.Add(user);
}
@ -113,13 +113,13 @@ public partial class UsersPage : ContentPage
private void OnSearchTextChanged(object sender, TextChangedEventArgs e)
{
var searchText = e.NewTextValue?.ToLower() ?? string.Empty;
string searchText = e.NewTextValue?.ToLower() ?? string.Empty;
if (string.IsNullOrWhiteSpace(searchText))
{
// Показываем всех пользователей
_users.Clear();
foreach (var user in _allUsers.OrderBy(u => u.FullName))
foreach (User user in _allUsers.OrderBy(u => u.FullName))
{
_users.Add(user);
}
@ -127,7 +127,7 @@ public partial class UsersPage : ContentPage
else
{
// Фильтруем пользователей
var filtered = _allUsers.Where(u =>
List<User> filtered = _allUsers.Where(u =>
u.FullName.ToLower().Contains(searchText) ||
u.Username.ToLower().Contains(searchText) ||
u.DepartmentName.ToLower().Contains(searchText) ||
@ -135,7 +135,7 @@ public partial class UsersPage : ContentPage
).ToList();
_users.Clear();
foreach (var user in filtered.OrderBy(u => u.FullName))
foreach (User user in filtered.OrderBy(u => u.FullName))
{
_users.Add(user);
}
@ -147,14 +147,14 @@ public partial class UsersPage : ContentPage
// Обновляем статус пользователя в списке
MainThread.BeginInvokeOnMainThread(() =>
{
var user = _users.FirstOrDefault(u => u.Id == updatedUser.Id);
User? user = _users.FirstOrDefault(u => u.Id == updatedUser.Id);
if (user != null)
{
user.IsOnline = updatedUser.IsOnline;
}
// Обновляем в основном списке
var allUser = _allUsers?.FirstOrDefault(u => u.Id == updatedUser.Id);
User? allUser = _allUsers?.FirstOrDefault(u => u.Id == updatedUser.Id);
if (allUser != null)
{
allUser.IsOnline = updatedUser.IsOnline;

View File

@ -10,7 +10,7 @@ class Program : MauiApplication
static void Main(string[] args)
{
var app = new Program();
Program app = new Program();
app.Run(args);
}
}

View File

@ -126,9 +126,9 @@ public class ApiService : IDisposable
Trace.WriteLine($"║ URI: {request.RequestUri}");
Trace.WriteLine($"║ BaseAddress: {_httpClient.BaseAddress}");
Trace.WriteLine($"║ Headers:");
foreach (var header in request.Headers)
foreach (KeyValuePair<string, IEnumerable<string>> header in request.Headers)
{
var value = header.Key == "Authorization" ? "[HIDDEN]" : string.Join(", ", header.Value);
string value = header.Key == "Authorization" ? "[HIDDEN]" : string.Join(", ", header.Value);
Trace.WriteLine($"║ {header.Key}: {value}");
}
if (request.Content != null)
@ -260,7 +260,7 @@ public class ApiService : IDisposable
{
var loginData = new { email, password };
// Используем относительный путь, так как BaseAddress уже установлен
var path = _config.TokenObtainPath.TrimStart('/');
string path = _config.TokenObtainPath.TrimStart('/');
Trace.WriteLine($"\n[ApiService] >>> Начинаем логин для {email}");
Trace.WriteLine($"[ApiService] >>> Путь токена: {_config.TokenObtainPath} → {path}");
@ -268,7 +268,7 @@ public class ApiService : IDisposable
Trace.WriteLine($"[ApiService] >>> Ожидаемый URL: {_httpClient.BaseAddress}{path}\n");
// 🔓 Создаем запрос БЕЗ Authorization header (это запрос для получения токена!)
var request = new HttpRequestMessage(HttpMethod.Post, path)
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, path)
{
Content = JsonContent.Create(loginData)
};
@ -278,13 +278,13 @@ public class ApiService : IDisposable
LogHttpDetails("LOGIN REQUEST", request, null, $"Email: {email}");
var response = await _httpClient.SendAsync(request);
HttpResponseMessage response = await _httpClient.SendAsync(request);
LogHttpDetails("LOGIN RESPONSE", request, response);
if (!response.IsSuccessStatusCode)
{
var errorText = await response.Content.ReadAsStringAsync();
string errorText = await response.Content.ReadAsStringAsync();
Trace.WriteLine($"\n[ApiService] ❌ Ошибка логина:");
Trace.WriteLine($" Status Code: {(int)response.StatusCode} {response.StatusCode}");
Trace.WriteLine($" Request URL: {response.RequestMessage?.RequestUri}");
@ -298,7 +298,7 @@ public class ApiService : IDisposable
}
// Проверяем Content-Type
var contentType = response.Content.Headers.ContentType?.MediaType ?? "";
string contentType = response.Content.Headers.ContentType?.MediaType ?? "";
if (!contentType.Contains("application/json"))
{
Trace.WriteLine($"[ApiService] ❌ Неверный Content-Type: {contentType}");
@ -311,10 +311,10 @@ public class ApiService : IDisposable
try
{
var responseBody = await response.Content.ReadAsStringAsync();
string responseBody = await response.Content.ReadAsStringAsync();
Trace.WriteLine($"\n[ApiService] Response Body: {responseBody}\n");
var tokenData = JsonSerializer.Deserialize<TokenResponse>(responseBody, new JsonSerializerOptions
TokenResponse? tokenData = JsonSerializer.Deserialize<TokenResponse>(responseBody, new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
});
@ -338,7 +338,7 @@ public class ApiService : IDisposable
// Получаем информацию о пользователе
Trace.WriteLine($"[ApiService] >>> Получаем информацию о пользователе...\n");
var user = await GetCurrentUserAsync();
User? user = await GetCurrentUserAsync();
if (user != null)
{
@ -409,7 +409,7 @@ public class ApiService : IDisposable
Trace.WriteLine("╠════════════════════════════════════════════════════════════════════════════════════");
// Используем относительный путь, так как BaseAddress уже установлен
var path = _config.TokenRefreshPath.TrimStart('/');
string path = _config.TokenRefreshPath.TrimStart('/');
var refreshData = new { refresh = _refreshToken };
Trace.WriteLine($"║ Request Path: {path}");
@ -418,21 +418,21 @@ public class ApiService : IDisposable
Trace.WriteLine("╚════════════════════════════════════════════════════════════════════════════════════");
// Создаем запрос БЕЗ авторизации (для refresh используется только refresh токен в теле)
var request = new HttpRequestMessage(HttpMethod.Post, path)
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, path)
{
Content = JsonContent.Create(refreshData)
};
var response = await _httpClient.SendAsync(request);
HttpResponseMessage response = await _httpClient.SendAsync(request);
Trace.WriteLine($"\n[ApiService] Refresh response: {(int)response.StatusCode} {response.StatusCode}");
if (response.IsSuccessStatusCode)
{
var responseText = await response.Content.ReadAsStringAsync();
string responseText = await response.Content.ReadAsStringAsync();
Trace.WriteLine($"[ApiService] Response body: {responseText}");
var tokenData = JsonSerializer.Deserialize<TokenResponse>(responseText, new JsonSerializerOptions
TokenResponse? tokenData = JsonSerializer.Deserialize<TokenResponse>(responseText, new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
});
@ -480,7 +480,7 @@ public class ApiService : IDisposable
}
else
{
var errorText = await response.Content.ReadAsStringAsync();
string errorText = await response.Content.ReadAsStringAsync();
Trace.WriteLine($"[ApiService] ❌ Refresh failed: {response.StatusCode}");
Trace.WriteLine($" Error: {errorText}");
}
@ -526,17 +526,17 @@ public class ApiService : IDisposable
}
// Используем относительный путь, так как BaseAddress установлен
var relativePath = path.TrimStart('/');
string relativePath = path.TrimStart('/');
// Добавляем query параметры
if (queryParams != null && queryParams.Any())
{
var query = string.Join("&", queryParams.Select(kvp =>
string query = string.Join("&", queryParams.Select(kvp =>
$"{Uri.EscapeDataString(kvp.Key)}={Uri.EscapeDataString(kvp.Value)}"));
relativePath = $"{relativePath}?{query}";
}
var request = new HttpRequestMessage(method, relativePath);
HttpRequestMessage request = new HttpRequestMessage(method, relativePath);
// 🔑 АВТОМАТИЧЕСКИ добавляем Authorization header если есть токен
if (requiresAuth && !string.IsNullOrEmpty(_accessToken))
@ -548,7 +548,7 @@ public class ApiService : IDisposable
// Добавляем дополнительные заголовки
if (headers != null)
{
foreach (var header in headers)
foreach (KeyValuePair<string, string> header in headers)
{
request.Headers.TryAddWithoutValidation(header.Key, header.Value);
}
@ -570,7 +570,7 @@ public class ApiService : IDisposable
Trace.WriteLine($" Has Body: Yes");
}
var response = await _httpClient.SendAsync(request);
HttpResponseMessage response = await _httpClient.SendAsync(request);
Trace.WriteLine($"[ApiService] <<< Response: {(int)response.StatusCode} {response.StatusCode}");
Trace.WriteLine($" Content-Type: {response.Content.Headers.ContentType?.MediaType ?? "N/A"}\n");
@ -600,7 +600,7 @@ public class ApiService : IDisposable
}
}
var responseText = await response.Content.ReadAsStringAsync();
string responseText = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode)
{
@ -713,12 +713,12 @@ public class ApiService : IDisposable
{
try
{
var queryParams = new Dictionary<string, string>();
Dictionary<string, string> queryParams = new Dictionary<string, string>();
if (page.HasValue) queryParams["page"] = page.Value.ToString();
if (pageSize.HasValue) queryParams["page_size"] = pageSize.Value.ToString();
if (!string.IsNullOrEmpty(search)) queryParams["search"] = search;
var response = await GetAsync<PaginatedResponse<User>>("api/v1/employees/", queryParams);
ApiResponse<PaginatedResponse<User>> response = await GetAsync<PaginatedResponse<User>>("api/v1/employees/", queryParams);
return response.Ok && response.Data != null ? response.Data : new PaginatedResponse<User>();
}
catch (Exception ex)
@ -735,7 +735,7 @@ public class ApiService : IDisposable
{
try
{
var allEmployees = new List<User>();
List<User> allEmployees = new();
int currentPage = 1;
PaginatedResponse<User>? response;
@ -765,7 +765,7 @@ public class ApiService : IDisposable
{
try
{
var response = await GetAsync<User>("api/v1/employees/me/");
ApiResponse<User> response = await GetAsync<User>("api/v1/employees/me/");
return response.Ok ? response.Data : null;
}
catch (Exception ex)
@ -782,7 +782,7 @@ public class ApiService : IDisposable
{
try
{
var response = await GetAsync<User>($"api/v1/employees/{id}/");
ApiResponse<User> response = await GetAsync<User>($"api/v1/employees/{id}/");
return response.Ok ? response.Data : null;
}
catch (Exception ex)
@ -803,11 +803,11 @@ public class ApiService : IDisposable
{
try
{
var queryParams = new Dictionary<string, string>();
Dictionary<string, string> queryParams = new Dictionary<string, string>();
if (page.HasValue) queryParams["page"] = page.Value.ToString();
if (pageSize.HasValue) queryParams["page_size"] = pageSize.Value.ToString();
var response = await GetAsync<PaginatedResponse<Department>>("api/v1/departments/", queryParams);
ApiResponse<PaginatedResponse<Department>> response = await GetAsync<PaginatedResponse<Department>>("api/v1/departments/", queryParams);
return response.Ok && response.Data != null ? response.Data : new PaginatedResponse<Department>();
}
catch (Exception ex)
@ -824,7 +824,7 @@ public class ApiService : IDisposable
{
try
{
var allDepartments = new List<Department>();
List<Department> allDepartments = new List<Department>();
int currentPage = 1;
PaginatedResponse<Department>? response;
@ -858,7 +858,7 @@ public class ApiService : IDisposable
{
try
{
var response = await GetAsync<List<Position>>("api/v1/positions/");
ApiResponse<List<Position>> response = await GetAsync<List<Position>>("api/v1/positions/");
return response.Ok && response.Data != null ? response.Data : new List<Position>();
}
catch (Exception ex)
@ -879,7 +879,7 @@ public class ApiService : IDisposable
{
try
{
var response = await GetAsync<List<Skill>>("api/v1/skills/");
ApiResponse<List<Skill>> response = await GetAsync<List<Skill>>("api/v1/skills/");
return response.Ok && response.Data != null ? response.Data : new List<Skill>();
}
catch (Exception ex)
@ -900,7 +900,7 @@ public class ApiService : IDisposable
{
try
{
var response = await GetAsync<List<CalendarEvent>>("api/v1/calendar/events/");
ApiResponse<List<CalendarEvent>> response = await GetAsync<List<CalendarEvent>>("api/v1/calendar/events/");
return response.Ok && response.Data != null ? response.Data : new List<CalendarEvent>();
}
catch (Exception ex)
@ -921,11 +921,11 @@ public class ApiService : IDisposable
{
try
{
var queryParams = new Dictionary<string, string>();
Dictionary<string, string> queryParams = new Dictionary<string, string>();
if (page.HasValue) queryParams["page"] = page.Value.ToString();
if (pageSize.HasValue) queryParams["page_size"] = pageSize.Value.ToString();
var response = await GetAsync<PaginatedResponse<Document>>("api/v1/documents/", queryParams);
ApiResponse<PaginatedResponse<Document>> response = await GetAsync<PaginatedResponse<Document>>("api/v1/documents/", queryParams);
return response.Ok && response.Data != null ? response.Data : new PaginatedResponse<Document>();
}
catch (Exception ex)
@ -946,11 +946,11 @@ public class ApiService : IDisposable
{
try
{
var queryParams = new Dictionary<string, string>();
Dictionary<string, string> queryParams = new Dictionary<string, string>();
if (page.HasValue) queryParams["page"] = page.Value.ToString();
if (pageSize.HasValue) queryParams["page_size"] = pageSize.Value.ToString();
var response = await GetAsync<PaginatedResponse<Request>>("api/v1/requests/", queryParams);
ApiResponse<PaginatedResponse<Request>> response = await GetAsync<PaginatedResponse<Request>>("api/v1/requests/", queryParams);
return response.Ok && response.Data != null ? response.Data : new PaginatedResponse<Request>();
}
catch (Exception ex)
@ -971,7 +971,7 @@ public class ApiService : IDisposable
{
try
{
var response = await GetAsync<List<EmployeeAction>>("api/v1/employee-actions/");
ApiResponse<List<EmployeeAction>> response = await GetAsync<List<EmployeeAction>>("api/v1/employee-actions/");
return response.Ok && response.Data != null ? response.Data : new List<EmployeeAction>();
}
catch (Exception ex)
@ -992,11 +992,11 @@ public class ApiService : IDisposable
{
try
{
var queryParams = new Dictionary<string, string>();
Dictionary<string, string> queryParams = new Dictionary<string, string>();
if (page.HasValue) queryParams["page"] = page.Value.ToString();
if (pageSize.HasValue) queryParams["page_size"] = pageSize.Value.ToString();
var response = await GetAsync<PaginatedResponse<Post>>("api/v1/posts/", queryParams);
ApiResponse<PaginatedResponse<Post>> response = await GetAsync<PaginatedResponse<Post>>("api/v1/posts/", queryParams);
return response.Ok && response.Data != null ? response.Data : new PaginatedResponse<Post>();
}
catch (Exception ex)
@ -1017,12 +1017,12 @@ public class ApiService : IDisposable
{
try
{
var queryParams = new Dictionary<string, string>();
Dictionary<string, string> queryParams = new Dictionary<string, string>();
if (page.HasValue) queryParams["page"] = page.Value.ToString();
if (pageSize.HasValue) queryParams["page_size"] = pageSize.Value.ToString();
if (postId.HasValue) queryParams["post"] = postId.Value.ToString();
var response = await GetAsync<PaginatedResponse<Comment>>("api/v1/comments/", queryParams);
ApiResponse<PaginatedResponse<Comment>> response = await GetAsync<PaginatedResponse<Comment>>("api/v1/comments/", queryParams);
return response.Ok && response.Data != null ? response.Data : new PaginatedResponse<Comment>();
}
catch (Exception ex)
@ -1043,11 +1043,11 @@ public class ApiService : IDisposable
{
try
{
var queryParams = new Dictionary<string, string>();
Dictionary<string, string> queryParams = new Dictionary<string, string>();
if (page.HasValue) queryParams["page"] = page.Value.ToString();
if (pageSize.HasValue) queryParams["page_size"] = pageSize.Value.ToString();
var response = await GetAsync<PaginatedResponse<DepartmentRole>>("api/v1/department-roles/", queryParams);
ApiResponse<PaginatedResponse<DepartmentRole>> response = await GetAsync<PaginatedResponse<DepartmentRole>>("api/v1/department-roles/", queryParams);
return response.Ok && response.Data != null ? response.Data : new PaginatedResponse<DepartmentRole>();
}
catch (Exception ex)
@ -1078,7 +1078,7 @@ public class ApiService : IDisposable
? $"api/v1/messages/?user_id={userId.Value}"
: "api/v1/messages/";
var response = await GetAsync<List<Message>>(path);
ApiResponse<List<Message>> response = await GetAsync<List<Message>>(path);
return response.Ok && response.Data != null ? response.Data : new List<Message>();
}
catch (Exception ex)
@ -1095,7 +1095,7 @@ public class ApiService : IDisposable
{
try
{
var response = await PostAsync<Message>("api/v1/messages/", message);
ApiResponse<Message> response = await PostAsync<Message>("api/v1/messages/", message);
return response.Ok;
}
catch (Exception ex)
@ -1112,8 +1112,8 @@ public class ApiService : IDisposable
{
try
{
var queryParams = new Dictionary<string, string> { { "q", query } };
var response = await GetAsync<List<User>>("api/v1/users/search/", queryParams);
Dictionary<string, string> queryParams = new() { { "q", query } };
ApiResponse<List<User>> response = await GetAsync<List<User>>("api/v1/users/search/", queryParams);
return response.Ok && response.Data != null ? response.Data : new List<User>();
}
catch (Exception ex)
@ -1130,7 +1130,7 @@ public class ApiService : IDisposable
{
try
{
var response = await PutAsync<object>("api/v1/users/status/", new { isOnline });
ApiResponse<object> response = await PutAsync<object>("api/v1/users/status/", new { isOnline });
return response.Ok;
}
catch (Exception ex)
@ -1158,23 +1158,23 @@ public class ApiService : IDisposable
await EnsureTokensLoadedAsync();
// Добавляем токен, если есть
var request = new HttpRequestMessage(HttpMethod.Get, url);
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, url);
if (!string.IsNullOrEmpty(_accessToken))
{
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _accessToken);
}
var response = await _httpClient.SendAsync(request);
HttpResponseMessage response = await _httpClient.SendAsync(request);
if (response.IsSuccessStatusCode)
{
var mediaType = response.Content.Headers.ContentType?.MediaType;
string? mediaType = response.Content.Headers.ContentType?.MediaType;
if (mediaType != null && !mediaType.StartsWith("image/"))
{
Trace.WriteLine($"[ApiService] ⚠️ URL {url} вернул не изображение, а {mediaType}");
return null;
}
var bytes = await response.Content.ReadAsByteArrayAsync();
byte[] bytes = await response.Content.ReadAsByteArrayAsync();
Trace.WriteLine($"[ApiService] ✅ Загружено изображение {url} ({bytes.Length} bytes)");
return bytes;
}

View File

@ -54,7 +54,7 @@ public class WebSocketService
// Пробуем подключиться к разным URL по очереди
Exception? lastException = null;
foreach (var url in WebSocketUrls)
foreach (string url in WebSocketUrls)
{
try
{
@ -71,7 +71,7 @@ public class WebSocketService
_cancellationTokenSource = new CancellationTokenSource();
var uri = new Uri(url);
Uri uri = new Uri(url);
Trace.WriteLine($"[WebSocket] Connecting to: {uri}");
await _webSocket.ConnectAsync(uri, _cancellationTokenSource.Token);
@ -167,14 +167,14 @@ public class WebSocketService
try
{
var json = JsonConvert.SerializeObject(new
string json = JsonConvert.SerializeObject(new
{
type = "message",
data = message
});
var bytes = Encoding.UTF8.GetBytes(json);
var buffer = new ArraySegment<byte>(bytes);
byte[] bytes = Encoding.UTF8.GetBytes(json);
ArraySegment<byte> buffer = new ArraySegment<byte>(bytes);
await _webSocket.SendAsync(buffer, WebSocketMessageType.Text, true, _cancellationTokenSource.Token);
}
@ -194,14 +194,14 @@ public class WebSocketService
try
{
var json = JsonConvert.SerializeObject(new
string json = JsonConvert.SerializeObject(new
{
type = "typing",
data = new { recipientId, isTyping }
});
var bytes = Encoding.UTF8.GetBytes(json);
var buffer = new ArraySegment<byte>(bytes);
byte[] bytes = Encoding.UTF8.GetBytes(json);
ArraySegment<byte> buffer = new ArraySegment<byte>(bytes);
await _webSocket.SendAsync(buffer, WebSocketMessageType.Text, true, _cancellationTokenSource.Token);
}
@ -221,14 +221,14 @@ public class WebSocketService
try
{
var json = JsonConvert.SerializeObject(new
string json = JsonConvert.SerializeObject(new
{
type = "get_history",
data = new { userId }
});
var bytes = Encoding.UTF8.GetBytes(json);
var buffer = new ArraySegment<byte>(bytes);
byte[] bytes = Encoding.UTF8.GetBytes(json);
ArraySegment<byte> buffer = new ArraySegment<byte>(bytes);
await _webSocket.SendAsync(buffer, WebSocketMessageType.Text, true, _cancellationTokenSource.Token);
Trace.WriteLine($"[WebSocket] Запрошена история сообщений с пользователем {userId}");
@ -241,13 +241,13 @@ public class WebSocketService
private async Task ReceiveMessagesAsync()
{
var buffer = new byte[8192];
byte[] buffer = new byte[8192];
try
{
while (_webSocket?.State == WebSocketState.Open && _cancellationTokenSource is { Token.IsCancellationRequested: false })
{
var result = await _webSocket.ReceiveAsync(
WebSocketReceiveResult result = await _webSocket.ReceiveAsync(
new ArraySegment<byte>(buffer),
_cancellationTokenSource.Token);
@ -263,7 +263,7 @@ public class WebSocketService
break;
}
var json = Encoding.UTF8.GetString(buffer, 0, result.Count);
string json = Encoding.UTF8.GetString(buffer, 0, result.Count);
await ProcessMessageAsync(json);
}
}
@ -283,7 +283,7 @@ public class WebSocketService
{
try
{
var wrapper = JsonConvert.DeserializeObject<WebSocketMessage>(json);
WebSocketMessage? wrapper = JsonConvert.DeserializeObject<WebSocketMessage>(json);
await MainThread.InvokeOnMainThreadAsync(() =>
{
@ -295,12 +295,12 @@ public class WebSocketService
break;
case "message_history":
var history = JsonConvert.DeserializeObject<List<Message>>(wrapper.Data.ToString() ?? string.Empty);
List<Message>? history = JsonConvert.DeserializeObject<List<Message>>(wrapper.Data.ToString() ?? string.Empty);
if (history != null) MessageHistoryReceived?.Invoke(this, history);
break;
case "user_status":
var user = JsonConvert.DeserializeObject<User>(wrapper.Data.ToString() ?? string.Empty);
User? user = JsonConvert.DeserializeObject<User>(wrapper.Data.ToString() ?? string.Empty);
if (user != null) UserStatusChanged?.Invoke(this, user);
break;