diff --git a/App.xaml b/App.xaml
new file mode 100644
index 0000000..d78f895
--- /dev/null
+++ b/App.xaml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/App.xaml.cs b/App.xaml.cs
new file mode 100644
index 0000000..7661d7d
--- /dev/null
+++ b/App.xaml.cs
@@ -0,0 +1,37 @@
+
+
+using NotesDataAnalyst.NoteContent;
+
+namespace NotesDataAnalyst
+{
+ public partial class App : Application
+ {
+ public static List AllNotes { get; set; } = new List();
+
+ public static DatabaseContext Database { get; private set; }
+
+ public App()
+ {
+ InitializeComponent();
+ string dbPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "notes.db");
+ Database = new DatabaseContext(dbPath);
+ MainPage = new AppShell();
+ }
+
+ // Метод дл� добавлени� или �охранени� заметки
+ public static void SaveNote(Note note)
+ {
+ var existingNote = AllNotes.FirstOrDefault(n => n.Id == note.Id);
+ if (existingNote == null)
+ {
+ AllNotes.Add(note);
+ }
+ else
+ {
+ // Обновл�ем �уще�твующую заметку
+ existingNote.Title = note.Title;
+ existingNote.Text = note.Text;
+ }
+ }
+ }
+}
diff --git a/AppShell.xaml b/AppShell.xaml
new file mode 100644
index 0000000..96d0e53
--- /dev/null
+++ b/AppShell.xaml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/AppShell.xaml.cs b/AppShell.xaml.cs
new file mode 100644
index 0000000..4209173
--- /dev/null
+++ b/AppShell.xaml.cs
@@ -0,0 +1,74 @@
+namespace NotesDataAnalyst
+{
+ public partial class AppShell : Shell
+ {
+ public AppShell()
+ {
+ InitializeComponent();
+ CategoryManager.LoadCategories(); // Загружаем категории при запу�ке
+ // Реги�траци� маршрута дл� MainPage
+ Routing.RegisterRoute("MainPage", typeof(MainPage));
+ }
+
+ private async void OnCategoriesMenuClicked(object sender, EventArgs e)
+ {
+ // Открываем в�плывающее окно � возможно�тью управлени� категори�ми
+ string action = await DisplayActionSheet("Управление категори�ми", "Отмена", null,
+ "Выбрать категорию", "Добавить категорию", "Удалить категорию");
+
+ switch (action)
+ {
+ case "Выбрать категорию":
+ await SelectCategory();
+ break;
+ case "Добавить категорию":
+ await AddCategory();
+ break;
+ case "Удалить категорию":
+ await DeleteCategory();
+ break;
+ }
+ }
+
+ private async Task SelectCategory()
+ {
+ // В�плывающее окно дл� выбора категории
+ string selectedCategory = await DisplayActionSheet("Выберите категорию", "Отмена", null, CategoryManager.Categories.ToArray());
+ if (!string.IsNullOrEmpty(selectedCategory) && selectedCategory != "Отмена")
+ {
+ // У�танавливаем выбранную категорию
+ CategoryManager.ActiveCategory = selectedCategory;
+ // Переход к MainPage отно�ительно текущего �тека, без аб�олютного маршрута
+ await Shell.Current.GoToAsync("MainPage");
+ }
+ }
+
+ private async Task AddCategory()
+ {
+ // В�плывающее окно дл� ввода названи� новой категории
+ string newCategory = await DisplayPromptAsync("�ова� категори�", "Введите название категории:");
+ if (!string.IsNullOrWhiteSpace(newCategory) && !CategoryManager.Categories.Contains(newCategory))
+ {
+ CategoryManager.AddCategory(newCategory);
+ }
+ }
+
+ private async Task DeleteCategory()
+ {
+ // В�плывающее окно дл� выбора категории дл� удалени�
+ string categoryToDelete = await DisplayActionSheet("Удалить категорию", "Отмена", null, CategoryManager.Categories.ToArray());
+ if (!string.IsNullOrEmpty(categoryToDelete) && categoryToDelete != "Отмена")
+ {
+ // Удал�ем категорию
+ CategoryManager.Categories.Remove(categoryToDelete);
+
+ // Сбро� активной категории, е�ли она была удалена
+ if (CategoryManager.ActiveCategory == categoryToDelete)
+ {
+ CategoryManager.ActiveCategory = "В�е категории";
+ }
+ }
+ }
+ }
+
+}
diff --git a/GraphDrawable.cs b/GraphDrawable.cs
new file mode 100644
index 0000000..e715d49
--- /dev/null
+++ b/GraphDrawable.cs
@@ -0,0 +1,80 @@
+using Microsoft.Maui.Graphics;
+using System.Collections.Generic;
+
+namespace NotesDataAnalyst
+{
+ public class GraphDrawable : IDrawable
+ {
+ private readonly List _nodes;
+ private readonly List _connections;
+
+ public GraphDrawable(List nodes, List connections)
+ {
+ _nodes = nodes;
+ _connections = connections;
+ }
+
+ private void DrawConnection(ICanvas canvas, GraphConnection connection)
+ {
+ var startX = connection.FromNode.Position.X + connection.FromNode.Size.Width / 2;
+ var startY = connection.FromNode.Position.Y + connection.FromNode.Size.Height / 2;
+ var endX = connection.ToNode.Position.X + connection.ToNode.Size.Width / 2;
+ var endY = connection.ToNode.Position.Y + connection.ToNode.Size.Height / 2;
+
+ canvas.StrokeColor = Colors.Gray;
+ canvas.StrokeSize = 2;
+ canvas.DrawLine((float)startX, (float)startY, (float)endX, (float)endY);
+ }
+
+
+ private void DrawNode(ICanvas canvas, GraphNode node)
+ {
+ canvas.FillColor = node.BackgroundColor;
+ canvas.FillRectangle((float)node.Position.X, (float)node.Position.Y, (float)node.Size.Width, (float)node.Size.Height);
+
+ canvas.FontColor = Colors.Black;
+ canvas.DrawString(node.Text, (float)node.Position.X + 10, (float)node.Position.Y + 20, HorizontalAlignment.Left);
+ }
+
+ public void Draw(ICanvas canvas, RectF dirtyRect)
+ {
+ canvas.StrokeColor = Colors.Black;
+ canvas.StrokeSize = 2;
+
+ // Отри�овка �в�зей
+ foreach (var connection in _connections)
+ {
+ canvas.DrawLine(
+ (float)connection.FromNode.Position.X, (float)connection.FromNode.Position.Y,
+ (float)connection.ToNode.Position.X, (float)connection.ToNode.Position.Y);
+ }
+
+ // Отри�овка узлов
+ foreach (var node in _nodes)
+ {
+ // Заливка фона узла
+ canvas.FillColor = node.BackgroundColor;
+ canvas.FillRectangle((float)node.Position.X, (float)node.Position.Y, (float)node.Size.Width, (float)node.Size.Height);
+
+ // Отри�овка тек�та
+ canvas.FontColor = Colors.Black;
+ canvas.DrawString(node.Text, (float)node.Position.X + 5, (float)node.Position.Y + 5, HorizontalAlignment.Left);
+
+ // Е�ли нода выбрана, ри�уем пунктирную обводку и "ручку" дл� изменени� размера
+ if (node.IsActive)
+ {
+ canvas.StrokeColor = Colors.Blue;
+ canvas.StrokeDashPattern = new float[] { 4, 2 };
+ canvas.DrawRectangle((float)node.Position.X, (float)node.Position.Y, (float)node.Size.Width, (float)node.Size.Height);
+
+ // Ручка дл� изменени� размера
+ canvas.FillColor = Colors.Gray;
+ canvas.FillRectangle(
+ (float)(node.Position.X + node.Size.Width - 10),
+ (float)(node.Position.Y + node.Size.Height - 10),
+ 10, 10);
+ }
+ }
+ }
+ }
+}
diff --git a/GraphNode.cs b/GraphNode.cs
new file mode 100644
index 0000000..fa4a11d
--- /dev/null
+++ b/GraphNode.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace NotesDataAnalyst
+{
+ public class GraphNode
+ {
+ public bool IsActive { get; set; }
+ public string Id { get; } = Guid.NewGuid().ToString(); // Уникальный идентификатор
+ public string Text { get; set; }
+ public Color BackgroundColor { get; set; } = Colors.LightBlue;
+ public Point Position { get; set; } // Позици� узла
+ public Size Size { get; set; } = new Size(100, 50); // Размер узла
+ public List Children { get; } = new List(); // Спи�ок дочерних узлов
+ }
+
+ public class GraphConnection
+ {
+ public GraphNode FromNode { get; set; } // Узел начала �в�зи
+ public GraphNode ToNode { get; set; } // Узел конца �в�зи
+ }
+}
diff --git a/GraphPage.xaml b/GraphPage.xaml
new file mode 100644
index 0000000..79fb8ce
--- /dev/null
+++ b/GraphPage.xaml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/GraphPage.xaml.cs b/GraphPage.xaml.cs
new file mode 100644
index 0000000..c3f9f78
--- /dev/null
+++ b/GraphPage.xaml.cs
@@ -0,0 +1,310 @@
+using System.Diagnostics;
+using System.Linq;
+
+namespace NotesDataAnalyst
+{
+
+ public partial class GraphPage : ContentPage
+ {
+ private readonly Entry _textEditor; // Ïîëå äëÿ ââîäà òåêñòà
+ private PointF _initialNodePosition; // Õðàíèò íà÷àëüíóþ ïîçèöèþ íîäû
+ private PointF _initialTouchPosition; // Õðàíèò íà÷àëüíóþ ïîçèöèþ êàñàíèÿ
+ private SizeF _initialNodeSize; // Õðàíèò íà÷àëüíûé ðàçìåð íîäû
+
+ private readonly List _nodes = new();
+ private readonly List _connections = new();
+ private GraphNode _selectedNode;
+ private bool _isResizing = false;
+
+ public GraphPage()
+ {
+ InitializeComponent();
+
+ // Ñîçäàåì ñêðûòîå ïîëå äëÿ ðåäàêòèðîâàíèÿ òåêñòà
+ _textEditor = new Entry
+ {
+ IsVisible = false,
+ BackgroundColor = Colors.Transparent,
+ FontSize = 18,
+ TextColor = Colors.Black
+ };
+ _textEditor.Completed += OnTextEditorCompleted; // Ñîõðàíÿåì òåêñò ïðè çàâåðøåíèè ââîäà
+ this.Add(_textEditor);
+
+ // Íàñòðàèâàåì Drawable äëÿ îòðèñîâêè óçëîâ è ñâÿçåé
+ var graphDrawable = new GraphDrawable(_nodes, _connections);
+ canvasView.Drawable = graphDrawable;
+
+ // Äîáàâëÿåì æåñòû
+ var panGesture = new PanGestureRecognizer();
+ panGesture.PanUpdated += OnPanUpdated;
+ canvasView.GestureRecognizers.Add(panGesture);
+
+ var tapGesture = new TapGestureRecognizer();
+ tapGesture.Tapped += OnCanvasTapped;
+ canvasView.GestureRecognizers.Add(tapGesture);
+ }
+
+ private void OnNodeTapped(object sender, TappedEventArgs e)
+ {
+ var node = _selectedNode;
+
+ var tapPosition = e.GetPosition((View)sender);
+ if (tapPosition.HasValue)
+ {
+ var touchPoint = new PointF((float)tapPosition.Value.X, (float)tapPosition.Value.Y);
+ _selectedNode = _nodes.FirstOrDefault(node => IsPointInsideNode(touchPoint, node));
+
+ if (_selectedNode != null)
+ {
+ // Ïîçèöèîíèðóåì è ïîêàçûâàåì òåêñòîâîå ïîëå
+ _textEditor.Text = _selectedNode.Text;
+ _textEditor.IsVisible = true;
+ _textEditor.Focus();
+ AbsoluteLayout.SetLayoutBounds(_textEditor, new Rect(_selectedNode.Position.X + 5, _selectedNode.Position.Y + 5, _selectedNode.Size.Width - 10, 30));
+ }
+ }
+
+ Debug.WriteLine("Íàæàòà íîäà - {node}");
+ }
+
+ private void OnTextEditorCompleted(object sender, EventArgs e)
+ {
+ // Ñîõðàíÿåì òåêñò â íîäó
+ if (_selectedNode != null)
+ {
+ _selectedNode.Text = _textEditor.Text;
+ _textEditor.IsVisible = false;
+ canvasView.Invalidate();
+ }
+ }
+
+
+
+ private void OnChildClicked(object sender, EventArgs e)
+ {
+ if (_selectedNode != null)
+ {
+ var child = new GraphNode
+ {
+ Text = "Child Node",
+ Position = new Point(_selectedNode.Position.X + 5, _selectedNode.Position.Y + 5),
+ Size = _selectedNode.Size,
+ BackgroundColor = Colors.Gray,
+ };
+ _nodes.Add(child);
+ _connections.Add(new GraphConnection { FromNode = _selectedNode, ToNode = child });
+ canvasView.Invalidate();
+
+ }
+ else { Debug.WriteLine("Ñíà÷àëà âûáèðèòå íîäó"); }
+ }
+
+ private async void OnAddNodeClicked(object sender, EventArgs e)
+ {
+
+ var newNode = new GraphNode
+ {
+ Text = "Íîâàÿ íîäà",
+ Position = new Point(300, 200),
+ Size = new Size(200, 200),
+
+ };
+
+ _nodes.Add(newNode);
+ canvasView.Invalidate();
+ }
+
+ // Îáðàáîò÷èê äëÿ âûáîðà óçëà
+ // Îáðàáîò÷èê äëÿ âûáîðà óçëà
+ private void OnCanvasTapped(object sender, TappedEventArgs e)
+ {
+ var tapPosition = e.GetPosition((View)sender);
+ _selectedNode = null;
+
+ if (tapPosition.HasValue)
+ {
+ var touchPoint = new PointF((float)tapPosition.Value.X, (float)tapPosition.Value.Y);
+ _selectedNode = _nodes.FirstOrDefault(node => IsPointInsideNode(touchPoint, node));
+
+ // Ñáðîñ àêòèâíîñòè äëÿ âñåõ óçëîâ, êðîìå âûáðàííîãî
+ foreach (var node in _nodes)
+ node.IsActive = node == _selectedNode;
+
+ // Îáíîâëÿåì ýêðàí ïîñëå èçìåíåíèÿ ñîñòîÿíèÿ óçëîâ
+ canvasView.Invalidate();
+ }
+ }
+
+
+ private void OnPanUpdated(object sender, PanUpdatedEventArgs e)
+ {
+ if (_selectedNode == null) return;
+
+ if (e.StatusType == GestureStatus.Started)
+ {
+ // Ñîõðàíÿåì íà÷àëüíóþ ïîçèöèþ íîäû è ðàçìåð
+ _initialNodePosition = _selectedNode.Position;
+ _initialTouchPosition = new PointF((float)e.TotalX, (float)e.TotalY);
+ _initialNodeSize = _selectedNode.Size; // Ñîõðàíÿåì íà÷àëüíûé ðàçìåð
+
+ // Ïðîâåðÿåì, íàõîäèìñÿ ëè ìû íà ðó÷êå èçìåíåíèÿ ðàçìåðà
+ _isResizing = IsOnRightResizeHandle(_selectedNode, _initialTouchPosition) ||
+ IsOnLeftResizeHandle(_selectedNode, _initialTouchPosition) ||
+ IsOnTopResizeHandle(_selectedNode, _initialTouchPosition) ||
+ IsOnBottomResizeHandle(_selectedNode, _initialTouchPosition);
+
+ }
+ else if (e.StatusType == GestureStatus.Running)
+ {
+ // Âû÷èñëÿåì ñìåùåíèå îò íà÷àëüíîé ïîçèöèè êàñàíèÿ
+ var deltaX = (float)e.TotalX - _initialTouchPosition.X;
+ var deltaY = (float)e.TotalY - _initialTouchPosition.Y;
+
+ if (_isResizing)
+ {
+ ResizeNode(_selectedNode, deltaX, deltaY);
+ }
+ else
+ {
+ // Ïåðåìåùåíèå
+ _selectedNode.Position = new PointF(
+ _initialNodePosition.X + deltaX,
+ _initialNodePosition.Y + deltaY
+ );
+ }
+
+ canvasView.Invalidate();
+ }
+ else if (e.StatusType == GestureStatus.Completed)
+ {
+ _isResizing = false;
+ }
+ }
+
+ // Èçìåíåíèå ðàçìåðà óçëà
+ private void ResizeNode(GraphNode node, double deltaX, double deltaY)
+ {
+ const float minSize = 50f; // Óáèðàåì "f" äëÿ double
+
+ // Óâåëè÷èâàåì èëè óìåíüøàåì ðàçìåð â çàâèñèìîñòè îò íàïðàâëåíèÿ
+ if (_isResizing)
+ {
+ // Åñëè ìû òÿíåì ïðàâóþ "ðó÷êó", óâåëè÷èâàåì øèðèíó
+ if (IsOnRightResizeHandle(node, _initialTouchPosition))
+ {
+ _selectedNode.Size = new Size(
+ Math.Max(minSize, node.Size.Width + deltaX), // double
+ _selectedNode.Size.Height // double
+ );
+ }
+ // Åñëè ìû òÿíåì ëåâóþ "ðó÷êó", óìåíüøàåì øèðèíó
+ else if (IsOnLeftResizeHandle(node, _initialTouchPosition))
+ {
+ _selectedNode.Size = new Size(
+ Math.Max(minSize, _selectedNode.Size.Width - deltaX), // double
+ _selectedNode.Size.Height // double
+ );
+
+ // Ïåðåìåùàåì íîäó âëåâî, ÷òîáû ñîõðàíèòü åå ïîçèöèþ
+ _selectedNode.Position = new Point(_selectedNode.Position.X + deltaX, _selectedNode.Position.Y); // double
+ }
+ // Åñëè ìû òÿíåì âåðõíþþ "ðó÷êó", óìåíüøàåì âûñîòó
+ else if (IsOnTopResizeHandle(node, _initialTouchPosition))
+ {
+ _selectedNode.Size = new Size(
+ _selectedNode.Size.Width, // double
+ Math.Max(minSize, _selectedNode.Size.Height - deltaY) // double
+ );
+
+ // Ïåðåìåùàåì íîäó ââåðõ, ÷òîáû ñîõðàíèòü åå ïîçèöèþ
+ _selectedNode.Position = new Point(_selectedNode.Position.X, _selectedNode.Position.Y + deltaY); // double
+ }
+ // Åñëè ìû òÿíåì íèæíþþ "ðó÷êó", óâåëè÷èâàåì âûñîòó
+ else if (IsOnBottomResizeHandle(node, _initialTouchPosition))
+ {
+ _selectedNode.Size = new Size(
+ _selectedNode.Size.Width, // double
+ Math.Max(minSize, _selectedNode.Size.Height + deltaY) // double
+ );
+ }
+ }
+
+
+
+
+ }
+
+
+ private bool IsOnRightResizeHandle(GraphNode node, PointF point)
+ {
+ const float handleSize = 400;
+ return Math.Abs(point.X - (node.Position.X + node.Size.Width)) <= handleSize;
+ }
+
+ private bool IsOnLeftResizeHandle(GraphNode node, PointF point)
+ {
+ const float handleSize = 400;
+ return Math.Abs(point.X - node.Position.X) <= handleSize;
+ }
+
+ private bool IsOnTopResizeHandle(GraphNode node, PointF point)
+ {
+ const float handleSize = 400;
+ return Math.Abs(point.Y - node.Position.Y) <= handleSize;
+ }
+
+ private bool IsOnBottomResizeHandle(GraphNode node, PointF point)
+ {
+ const float handleSize = 400;
+ return Math.Abs(point.Y - (node.Position.Y + node.Size.Height)) <= handleSize;
+ }
+
+
+ // Ìåòîä äëÿ ïðîâåðêè, íàõîäèòñÿ ëè òî÷êà âíóòðè óçëà
+ private bool IsPointInsideNode(PointF point, GraphNode node)
+ {
+ return point.X >= node.Position.X &&
+ point.X <= node.Position.X + node.Size.Width &&
+ point.Y >= node.Position.Y &&
+ point.Y <= node.Position.Y + node.Size.Height;
+ }
+
+
+ // Ïðîâåðêà, íàõîäèòñÿ ëè òî÷êà íà "ðó÷êå" èçìåíåíèÿ ðàçìåðà
+ private bool IsOnResizeHandle(GraphNode node, PointF point)
+ {
+ const float handleSize = 200f; // Ðàçìåð ðó÷êè èçìåíåíèÿ ðàçìåðà
+
+ // Îïðåäåëÿåì ïðÿìîóãîëüíèêè äëÿ êàæäîé ðó÷êè
+ var rightHandleRect = new RectF(
+ (float)(node.Position.X + node.Size.Width - handleSize),
+ (float)(node.Position.Y + node.Size.Height - handleSize),
+ handleSize, handleSize);
+
+ var leftHandleRect = new RectF(
+ (float)(node.Position.X),
+ (float)(node.Position.Y + node.Size.Height - handleSize),
+ handleSize, handleSize);
+
+ var topHandleRect = new RectF(
+ (float)(node.Position.X + (node.Size.Width / 2) - (handleSize / 2)),
+ (float)(node.Position.Y),
+ handleSize, handleSize);
+
+ var bottomHandleRect = new RectF(
+ (float)(node.Position.X + (node.Size.Width / 2) - (handleSize / 2)),
+ (float)(node.Position.Y + node.Size.Height - handleSize),
+ handleSize, handleSize);
+
+ // Ïðîâåðÿåì, íàõîäèòñÿ ëè êóðñîð íà îäíîé èç ðó÷åê
+ return rightHandleRect.Contains(point) ||
+ leftHandleRect.Contains(point) ||
+ topHandleRect.Contains(point) ||
+ bottomHandleRect.Contains(point);
+ }
+
+
+ }
+}
+
diff --git a/MauiProgram.cs b/MauiProgram.cs
new file mode 100644
index 0000000..63569a0
--- /dev/null
+++ b/MauiProgram.cs
@@ -0,0 +1,22 @@
+using Microsoft.Extensions.Logging;
+using CommunityToolkit.Maui;
+
+namespace NotesDataAnalyst
+{
+ public static class MauiProgram
+ {
+ public static MauiApp CreateMauiApp()
+ {
+ var builder = MauiApp.CreateBuilder();
+ builder
+ .UseMauiApp()
+ .UseMauiCommunityToolkit() // Подключение CommunityToolkit.Maui
+ .ConfigureFonts(fonts =>
+ {
+ fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
+ });
+
+ return builder.Build();
+ }
+ }
+}
diff --git a/NoteContent/CategoryManager.cs b/NoteContent/CategoryManager.cs
new file mode 100644
index 0000000..00cfbff
--- /dev/null
+++ b/NoteContent/CategoryManager.cs
@@ -0,0 +1,48 @@
+using System.Collections.ObjectModel;
+using Microsoft.Maui.Storage;
+
+public static class CategoryManager
+{
+ public static ObservableCollection Categories { get; private set; } = new ObservableCollection();
+
+ private const string ActiveCategoryKey = "ActiveCategory";
+
+ public static string ActiveCategory
+ {
+ get => Preferences.Get(ActiveCategoryKey, Categories.FirstOrDefault() ?? ""); // Проверка на null
+ set
+ {
+ if (!string.IsNullOrEmpty(value))
+ {
+ Preferences.Set(ActiveCategoryKey, value);
+ }
+ }
+ }
+
+ public static void LoadCategories()
+ {
+ if (Categories.Count == 0) // Проверка, чтобы не загружать повторно
+ {
+ Categories.Add("Личное");
+ Categories.Add("Работа");
+ Categories.Add("Дом");
+
+ // У�танавливаем первую категорию как активную, е�ли она не задана
+ if (string.IsNullOrEmpty(ActiveCategory))
+ {
+ ActiveCategory = Categories.First();
+ }
+ }
+ }
+
+ public static void AddCategory(string category)
+ {
+ if (!Categories.Contains(category))
+ {
+ Categories.Add(category);
+ ActiveCategory = category; // Сразу делаем новую категорию активной
+ }
+ }
+}
+
+
diff --git a/NoteContent/DatabaseContext.cs b/NoteContent/DatabaseContext.cs
new file mode 100644
index 0000000..eef0e13
--- /dev/null
+++ b/NoteContent/DatabaseContext.cs
@@ -0,0 +1,126 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Microsoft.Data.Sqlite;
+using System.Collections.ObjectModel;
+namespace NotesDataAnalyst.NoteContent
+{
+
+
+ public class DatabaseContext
+ {
+ private readonly string _connectionString;
+
+ public DatabaseContext(string dbPath)
+ {
+ _connectionString = $"Data Source={dbPath}";
+ InitializeDatabase();
+ }
+
+ private void InitializeDatabase()
+ {
+ using (var connection = new SqliteConnection(_connectionString))
+ {
+ connection.Open();
+
+ var command = connection.CreateCommand();
+ command.CommandText =
+ @"
+ CREATE TABLE IF NOT EXISTS Notes (
+ Id TEXT PRIMARY KEY,
+ Category TEXT,
+ Title TEXT,
+ Image TEXT,
+ Text TEXT,
+ DateCreated TEXT
+ );
+ ";
+ command.ExecuteNonQuery();
+ }
+ }
+
+ public ObservableCollection GetAllNotes()
+ {
+ var notes = new ObservableCollection();
+
+ using (var connection = new SqliteConnection(_connectionString))
+ {
+ connection.Open();
+
+ var command = connection.CreateCommand();
+ command.CommandText = "SELECT * FROM Notes";
+ using (var reader = command.ExecuteReader())
+ {
+ while (reader.Read())
+ {
+ var note = new Note
+ {
+ Id = reader.GetString(0),
+ Category = reader.GetString(1),
+ Title = reader.GetString(2),
+ Image = reader.GetString(3),
+ Text = reader.GetString(4), // Загружаем тек�т из БД
+ DateCreated = DateTime.TryParse(reader.GetString(5), out var date) ? date : DateTime.UtcNow
+ };
+
+ notes.Add(note);
+ }
+ }
+ }
+
+ return notes;
+ }
+
+ public void SaveNote(Note note)
+ {
+ using (var connection = new SqliteConnection(_connectionString))
+ {
+ connection.Open();
+
+ var command = connection.CreateCommand();
+ command.CommandText =
+ @"
+ INSERT OR REPLACE INTO Notes (Id, Category, Title, Image, Text, DateCreated)
+ VALUES ($id, $category, $title, $image, $text, $dateCreated)
+ ";
+ command.Parameters.AddWithValue("$id", note.Id);
+ command.Parameters.AddWithValue("$category", note.Category);
+ command.Parameters.AddWithValue("$title", note.Title);
+ command.Parameters.AddWithValue("$image", note.Image);
+ command.Parameters.AddWithValue("$text", note.Text);
+ command.Parameters.AddWithValue("$dateCreated", note.DateCreated.ToString("o")); // Сохран�ем в формате ISO
+ command.ExecuteNonQuery();
+
+ }
+ }
+
+ public void DeleteNote(Note note)
+ {
+ if (string.IsNullOrEmpty(note.Id))
+ {
+ Console.WriteLine("Ошибка: У заметки от�ут�твует Id.");
+ return;
+ }
+
+ using (var connection = new SqliteConnection(_connectionString))
+ {
+ connection.Open();
+
+ var command = connection.CreateCommand();
+ command.CommandText =
+ @"
+ DELETE FROM Notes
+ WHERE Id = $id
+ ";
+ command.Parameters.AddWithValue("$id", note.Id);
+ command.ExecuteNonQuery();
+ int result = command.ExecuteNonQuery();
+
+ Console.WriteLine(result > 0 ? "Заметка удалена из базы данных." : "Ошибка: Заметка не найдена в базе данных.");
+ }
+ }
+ }
+
+}
diff --git a/NoteContent/EditorPage.xaml b/NoteContent/EditorPage.xaml
new file mode 100644
index 0000000..d16f0d2
--- /dev/null
+++ b/NoteContent/EditorPage.xaml
@@ -0,0 +1,61 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/NoteContent/EditorPage.xaml.cs b/NoteContent/EditorPage.xaml.cs
new file mode 100644
index 0000000..3594d95
--- /dev/null
+++ b/NoteContent/EditorPage.xaml.cs
@@ -0,0 +1,301 @@
+using CommunityToolkit.Maui.Views;
+using NotesDataAnalyst.NoteContent;
+using System.Text.RegularExpressions;
+using System.Windows.Input;
+
+namespace NotesDataAnalyst
+{
+ public partial class EditorPage : ContentPage
+ {
+ //Âûçûâàåì ýìîäçè
+ public ICommand ShowEmojiPopupCommand { get; }
+ public ICommand ShowLinkInputCommand { get; }
+
+ public ICommand InsertHeaderCommand { get; }
+
+ public ICommand AddLinkCommand { get; }
+
+ private int listItemCounter = 1; // Ñ÷¸ò÷èê äëÿ íóìåðàöèè â ñïèñêå
+
+
+ private readonly Note _note;
+ private readonly FileSaver _fileSaver;
+ private readonly string _filePath;
+
+ public EditorPage(Note note = null)
+ {
+ InitializeComponent();
+
+ // Èíèöèàëèçàöèÿ êîìàíä
+ // Èíèöèàëèçàöèÿ êîìàíä
+ InsertHeaderCommand = new Command(InsertHeaderTag);
+ // InsertBulletCommand = new Command(InsertBulletPoint);
+ AddLinkCommand = new Command(AddLink);
+ // InsertNumberedListCommand = new Command(InsertNumberedListItem);
+ ShowLinkInputCommand = new Command(ShowLinkInputPopup);
+ //Èíèöèëèçèðóåì ýìîäçè
+ ShowEmojiPopupCommand = new Command(ShowEmojiSelectionPopup);
+ BindingContext = this; // Óñòàíàâëèâàåì êîíòåêñò ïðèâÿçêè íà òåêóùèé îáúåêò ñòðàíèöû
+
+ _note = note ?? new Note(); // Åñëè note íå ïåðåäàí, ñîçäà¸ì íîâóþ çàìåòêó
+ _filePath = string.Empty; // Ïóñòîé ïóòü óêàçûâàåò íà ñîçäàíèå íîâîé çàìåòêè
+
+ // Çàïîëíÿåì äàííûå, åñëè ýòî ñóùåñòâóþùàÿ çàìåòêà
+ if (_note != null)
+ {
+ categoryPicker.SelectedItem = _note.Category;
+ titleEditor.Text = _note.Title;
+ textEditor.Text = _note.Text;
+
+ if (!string.IsNullOrEmpty(_note.Image))
+ {
+ imageButton.Source = ImageSource.FromFile(_note.Image);
+ }
+ }
+
+ // Íàñòðàèâàåì âûáîð êàòåãîðèè è óñòàíîâêó ôîêóñà
+ CategoryManager.LoadCategories();
+ categoryPicker.ItemsSource = CategoryManager.Categories;
+ categoryPicker.SelectedItem = CategoryManager.ActiveCategory;
+
+ string basePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Notes");
+ _fileSaver = new FileSaver(basePath);
+ }
+
+
+ // Ðàáîòàåì ñ ýìîäçè
+ private async void ShowEmojiSelectionPopup()
+ {
+ var emojiPopup = new EmojiSelectionPopup();
+
+ // Îáðàáîò÷èê âûáîðà ñìàéëèêà
+ emojiPopup.EmojiSelected += emoji =>
+ {
+ InsertEmoji(emoji);
+
+ // Èñïîëüçóåì Dispatcher äëÿ áåçîïàñíîãî âûçîâà Close
+ Application.Current.Dispatcher.Dispatch(() =>
+ {
+ if (emojiPopup.Handler != null) // Ïðîâåðêà, ÷òî Popup åùå íå çàêðûò
+ {
+ emojiPopup.Close();
+ }
+ });
+ };
+
+ // Îòîáðàæàåì Popup
+ await Application.Current.MainPage.ShowPopupAsync(emojiPopup);
+ }
+
+
+
+
+
+
+ private void InsertEmoji(string emoji)
+ {
+ int cursorPosition = textEditor.CursorPosition;
+ textEditor.Text = textEditor.Text.Insert(cursorPosition, emoji);
+ textEditor.CursorPosition = cursorPosition + emoji.Length;
+ }
+
+///////////////////////
+///
+
+ ///
+ /// //////////////////////////////////////////////////////////////
+ ///
+ ///
+
+ // Òýãè ìàðêäàóí
+ private void InsertHeaderTag()
+ {
+ if (textEditor == null)
+ {
+ Console.WriteLine("textEditor íå èíèöèàëèçèðîâàí.");
+ return;
+ }
+
+ // Ïðîâåðêà, åñëè òåêñò ïóñòîé
+ if (string.IsNullOrEmpty(textEditor.Text))
+ {
+ textEditor.Text = "# ";
+ textEditor.CursorPosition = textEditor.Text.Length;
+ return;
+ }
+
+ int cursorPosition = textEditor.CursorPosition;
+ string newText = cursorPosition == 0 || textEditor.Text[cursorPosition - 1] == '\n' ? "# " : "\n# ";
+
+ textEditor.Text = textEditor.Text.Insert(cursorPosition, newText);
+ textEditor.CursorPosition = cursorPosition + newText.Length;
+ }
+
+ private async void ShowLinkInputPopup()
+ {
+ var linkInputPopup = new LinkInputPopup();
+
+ // Ïîäïèñûâàåìñÿ íà ñîáûòèå, ÷òîáû ïîëó÷èòü ññûëêó è âñòàâèòü å¸ â òåêñò
+ linkInputPopup.LinkSubmitted += url =>
+ {
+ InsertLinkMarkdown(url);
+ };
+
+ // Îòêðûâàåì ìîäàëüíî
+ await Application.Current.MainPage.Navigation.PushModalAsync(linkInputPopup);
+ }
+
+ private void InsertLinkMarkdown(string url)
+ {
+ string markdownLink = $"";
+ int cursorPosition = textEditor.CursorPosition;
+ textEditor.Text = textEditor.Text.Insert(cursorPosition, markdownLink);
+ textEditor.CursorPosition = cursorPosition + markdownLink.Length;
+ }
+
+
+
+ /* private void InsertBulletPoint()
+ {
+ if (textEditor == null)
+ {
+ Console.WriteLine("textEditor íå èíèöèàëèçèðîâàí.");
+ return;
+ }
+
+ if (string.IsNullOrEmpty(textEditor.Text))
+ {
+ textEditor.Text = "* ";
+ textEditor.CursorPosition = textEditor.Text.Length;
+ return;
+ }
+
+ int cursorPosition = textEditor.CursorPosition;
+ string bulletText = "* ";
+ textEditor.Text = textEditor.Text.Insert(cursorPosition, bulletText);
+ textEditor.CursorPosition = cursorPosition + bulletText.Length;
+
+ textEditor.Completed += (s, e) =>
+ {
+ if (!HasTwoEmptyLines(cursorPosition))
+ {
+ cursorPosition = textEditor.CursorPosition;
+ textEditor.Text = textEditor.Text.Insert(cursorPosition, bulletText);
+ textEditor.CursorPosition = cursorPosition + bulletText.Length;
+ }
+ };
+ }
+
+
+ private void InsertNumberedListItem()
+ {
+ int cursorPosition = textEditor.CursorPosition;
+ string listItemText = $"{listItemCounter}. ";
+
+ textEditor.Text = textEditor.Text.Insert(cursorPosition, listItemText);
+ textEditor.CursorPosition = cursorPosition + listItemText.Length;
+
+ // Îáíîâëÿåì íîìåð ñïèñêà ïðè ïåðåõîäå íà íîâóþ ñòðîêó
+ textEditor.Completed += (s, e) =>
+ {
+ if (!HasTwoEmptyLines(cursorPosition))
+ {
+ cursorPosition = textEditor.CursorPosition;
+ listItemCounter++;
+ listItemText = $"{listItemCounter}. ";
+ textEditor.Text = textEditor.Text.Insert(cursorPosition, listItemText);
+ textEditor.CursorPosition = cursorPosition + listItemText.Length;
+ }
+ else
+ {
+ listItemCounter = 1; // Ñáðîñ ñ÷¸ò÷èêà ïðè äâóõ ïóñòûõ ñòðîêàõ
+ }
+ };
+ }
+
+ // Ïðîâåðêà íà äâå ïóñòûå ñòðîêè ïîñëå ïîçèöèè êóðñîðà
+ private bool HasTwoEmptyLines(int position)
+ {
+ string textAfterCursor = textEditor.Text.Substring(position);
+ return Regex.IsMatch(textAfterCursor, @"\n\s*\n\s*\n");
+ }*/
+
+ ////////////////////////////////////////////////////////////////////////////////////////////
+
+ private async void AddLink()
+ {
+ var popup = new NoteSelectionPopup();
+ popup.NoteSelected += OnNoteSelectedForLink;
+ await this.ShowPopupAsync(popup);
+
+ }
+
+ private void OnNoteSelectedForLink(Note selectedNote)
+ {
+ textEditor.Text = NoteLinkManager.InsertLink(textEditor.Text, selectedNote);
+ }
+
+
+ private async void OnImageTapped(object sender, EventArgs e)
+ {
+ try
+ {
+ var result = await FilePicker.PickAsync(new PickOptions
+ {
+ FileTypes = FilePickerFileType.Images,
+ PickerTitle = "Âûáåðèòå èçîáðàæåíèå"
+ });
+
+ if (result != null)
+ {
+ _note.Image = result.FullPath;
+ imageButton.Source = ImageSource.FromFile(result.FullPath);
+ }
+ }
+ catch (Exception ex)
+ {
+ await DisplayAlert("Îøèáêà", $"Íå óäàëîñü çàãðóçèòü èçîáðàæåíèå: {ex.Message}", "OK");
+ }
+ }
+
+ private async void SaveButton_Clicked(object sender, EventArgs e)
+ {
+ // Åñëè ó çàìåòêè íåò Id, ïðèñâàèâàåì íîâûé Id
+ if (string.IsNullOrEmpty(_note.Id))
+ {
+ _note.Id = Guid.NewGuid().ToString();
+ }
+
+ var note = new Note
+ { Id = _note.Id,
+ Category = categoryPicker.SelectedItem as string,
+ Title = titleEditor.Text,
+ Image = _note.Image,
+ Text = textEditor.Text,
+ FilePath = _filePath
+ };
+ // Ïðîâåðêà çàãîëîâêà
+ if (string.IsNullOrEmpty(note.Title))
+ {
+ // Ïîëó÷àåì ïåðâûå 4 ñëîâà èç òåêñòà
+ var words = note.Text.Split(' ', StringSplitOptions.RemoveEmptyEntries);
+ note.Title = string.Join(" ", words.Take(4));
+ }
+
+ //Çàïèñü â ÁÄ ïîêà îòêëþ÷åíà
+ await Task.Run(() => App.Database.SaveNote(note));
+ // Îòïðàâëÿåì ñîîáùåíèå äëÿ îáíîâëåíèÿ ñïèñêà çàìåòîê íà ãëàâíîé ñòðàíèöå
+ MessagingCenter.Send(this, "NoteSaved", _note);
+ await DisplayAlert("Success", "Note saved successfully.", "OK");
+ await Navigation.PushAsync(new MainPage());
+ }
+
+
+ }
+}
+
+
+
+
+
+
diff --git a/NoteContent/EmojiSelectionPopup.xaml b/NoteContent/EmojiSelectionPopup.xaml
new file mode 100644
index 0000000..4aad040
--- /dev/null
+++ b/NoteContent/EmojiSelectionPopup.xaml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/NoteContent/EmojiSelectionPopup.xaml.cs b/NoteContent/EmojiSelectionPopup.xaml.cs
new file mode 100644
index 0000000..5d7f937
--- /dev/null
+++ b/NoteContent/EmojiSelectionPopup.xaml.cs
@@ -0,0 +1,44 @@
+using CommunityToolkit.Maui.Views;
+using System.Collections.ObjectModel;
+
+namespace NotesDataAnalyst;
+
+public partial class EmojiSelectionPopup : Popup
+{
+ public event Action? EmojiSelected;
+
+ public ObservableCollection EmojiList { get; set; }
+
+ public EmojiSelectionPopup()
+ {
+ InitializeComponent();
+ EmojiList = new ObservableCollection
+ {
+ "�", "😊", "🎉", "�","📑","�","��","📆","💼","👜","💄","⌚","💎","��",
+ "��", "🔥", "✨", "🚀",
+ // Добавьте больше �майликов по мере необходимо�ти
+ };
+ BindingContext = this;
+ }
+
+ // Обработчик выбора �майлика
+
+
+
+ private void OnEmojiSelected(object sender, SelectionChangedEventArgs e)
+ {
+ if (e.CurrentSelection.FirstOrDefault() is string selectedEmoji)
+ {
+ EmojiSelected?.Invoke(selectedEmoji);
+ // Close(); // И�пользуем Close дл� закрыти� окна
+ }
+ }
+
+ private void OnCloseButtonClicked(object sender, EventArgs e)
+ {
+ Close(); // И�пользуем Close при нажатии на кнопку
+ }
+
+
+}
+
diff --git a/NoteContent/FileLoader.cs b/NoteContent/FileLoader.cs
new file mode 100644
index 0000000..7363e4f
--- /dev/null
+++ b/NoteContent/FileLoader.cs
@@ -0,0 +1,70 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Text.Json;
+using System.Threading.Tasks;
+
+/*namespace NotesDataAnalyst
+{
+ public static class FileLoader
+ {
+ // Загрузка одной заметки из указанного файла
+ public static async Task LoadDataAsync(string filePath)
+ {
+ if (!File.Exists(filePath))
+ {
+ return null; // Е�ли файл не �уще�твует, возвращаем null
+ }
+
+ try
+ {
+ // Чтение JSON-�одержимого из файла
+ string jsonString = await File.ReadAllTextAsync(filePath);
+
+ // Де�ериализаци� в объект Note
+ Note? note = JsonSerializer.Deserialize(jsonString);
+ if (note != null)
+ {
+ note.FilePath = filePath; // У�танавливаем путь к файлу, е�ли загрузка у�пешна
+ }
+
+ return note;
+ }
+ catch (JsonException ex)
+ {
+ Console.WriteLine($"Ошибка де�ериализации JSON: {ex.Message}");
+ return null;
+ }
+ }
+
+
+ // Загрузка в�ех заметок из базовой папки
+ /* public static async Task> LoadAllNotesAsync(string basePath)
+ {
+ var notes = new List();
+
+ // Провер�ем, �уще�твует ли базова� папка
+ if (!Directory.Exists(basePath))
+ {
+ return notes; // Е�ли папки нет, возвращаем пу�той �пи�ок
+ }
+
+ // Проходим по каждой папке категории
+ foreach (string categoryFolder in Directory.GetDirectories(basePath))
+ {
+ // Проходим по в�ем JSON-файлам в папке категории
+ foreach (string filePath in Directory.GetFiles(categoryFolder, "*.json"))
+ {
+ Note? note = await LoadDataAsync(filePath); // Загружаем заметку из файла
+ if (note != null)
+ {
+ notes.Add(note); // Добавл�ем в �пи�ок, е�ли у�пешно загружено
+ }
+ }
+ }
+
+ return notes; // Возвращаем �пи�ок в�ех загруженных заметок
+ }
+ }
+}*/
diff --git a/NoteContent/FileSaver.cs b/NoteContent/FileSaver.cs
new file mode 100644
index 0000000..546dbe7
--- /dev/null
+++ b/NoteContent/FileSaver.cs
@@ -0,0 +1,83 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.ComponentModel;
+using System.Linq;
+using System.Text;
+using System.Text.Json;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+
+namespace NotesDataAnalyst.NoteContent
+{
+ public class FileSaver : INotifyPropertyChanged
+ {
+ // Базовый путь, где будут хранить�� в�е заметки
+ private readonly string _basePath;
+ private ObservableCollection _files;
+
+ // Коллекци� дл� хранени� путей к �охранённым файлам
+ public ObservableCollection Files
+ {
+ get => _files;
+ private set
+ {
+ _files = value;
+ OnPropertyChanged(nameof(Files));
+ }
+ }
+
+ public FileSaver(string basePath)
+ {
+ // У�танавливаем базовый путь и инициализируем коллекцию файлов
+ _basePath = basePath;
+ Files = new ObservableCollection();
+ }
+
+ // Создаёт путь к файлу на о�нове категории и заголовка
+ private string GenerateFilePath(Note note)
+ {
+ // Определ�ем путь дл� папки категории
+ string categoryFolder = Path.Combine(_basePath, note.Category);
+
+ // Создаём папку, е�ли её ещё нет
+ Directory.CreateDirectory(categoryFolder);
+
+ // Преобразуем заголовок в им� файла, удал�� недопу�тимые �имволы
+ string fileName = SanitizeFileName(note.Title) + ".json";
+
+ // Полный путь к файлу
+ return Path.Combine(categoryFolder, fileName);
+ }
+
+ // Удал�ет недопу�тимые �имволы из заголовка дл� имени файла
+ private string SanitizeFileName(string title)
+ {
+ // Замен�ем �имволы, которые нельз� и�пользовать в именах файлов, на подчёркивани�
+ return Regex.Replace(title, @"[<>:""/\|?*]", "_");
+ }
+
+ public void SaveData(Note note)
+ {
+ // Е�ли у заметки е�ть путь к файлу, обновл�ем �уще�твующий файл
+ string filePath = !string.IsNullOrEmpty(note.FilePath)
+ ? note.FilePath
+ : GenerateFilePath(note);
+
+ string jsonString = JsonSerializer.Serialize(note);
+ File.WriteAllText(filePath, jsonString);
+
+ // Е�ли �то нова� заметка, добавл�ем её путь в коллекцию
+ if (!Files.Contains(filePath))
+ {
+ Files.Add(filePath);
+ }
+ }
+
+
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ protected virtual void OnPropertyChanged(string propertyName) =>
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+ }
+}
diff --git a/NoteContent/LinkInputPopup.xaml b/NoteContent/LinkInputPopup.xaml
new file mode 100644
index 0000000..c7c07e7
--- /dev/null
+++ b/NoteContent/LinkInputPopup.xaml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/NoteContent/LinkInputPopup.xaml.cs b/NoteContent/LinkInputPopup.xaml.cs
new file mode 100644
index 0000000..3c184a3
--- /dev/null
+++ b/NoteContent/LinkInputPopup.xaml.cs
@@ -0,0 +1,33 @@
+namespace NotesDataAnalyst;
+
+public partial class LinkInputPopup : ContentPage
+{
+ public event Action? LinkSubmitted;
+
+ public LinkInputPopup()
+ {
+ InitializeComponent();
+ }
+
+ private void OnAddButtonClicked(object sender, EventArgs e)
+ {
+ string url = urlEntry.Text;
+
+ if (!string.IsNullOrEmpty(url))
+ {
+ LinkSubmitted?.Invoke(url); // Ïåðåäà÷à ââåäåííîé ññûëêè îáðàòíî
+ ClosePopup();
+ }
+ }
+
+ private void OnCancelButtonClicked(object sender, EventArgs e)
+ {
+ ClosePopup();
+ }
+
+ private void ClosePopup()
+ {
+ // Çàêðûòèå îêíà (èìèòèðóåì ïîâåäåíèå Popup)
+ Application.Current.MainPage.Navigation.PopModalAsync();
+ }
+}
\ No newline at end of file
diff --git a/NoteContent/MainPage.xaml b/NoteContent/MainPage.xaml
new file mode 100644
index 0000000..2ef82d4
--- /dev/null
+++ b/NoteContent/MainPage.xaml
@@ -0,0 +1,86 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/NoteContent/MainPage.xaml.cs b/NoteContent/MainPage.xaml.cs
new file mode 100644
index 0000000..75b1f34
--- /dev/null
+++ b/NoteContent/MainPage.xaml.cs
@@ -0,0 +1,247 @@
+using System.Collections.ObjectModel;
+using NotesDataAnalyst.NoteContent;
+
+namespace NotesDataAnalyst
+{
+ // MainPage.xaml.cs
+
+
+ public partial class MainPage : ContentPage
+ {
+ public DateTime DateCreated { get; set; } = DateTime.UtcNow;
+ public ObservableCollection Notes { get; set; }
+ public ObservableCollection FilteredNotes { get; set; }
+
+ public MainPage()
+ {
+ InitializeComponent();
+
+
+ BindingContext = this;
+
+
+ // Подпи�ываем�� на �ообщени� дл� обновлени� �пи�ка заметок
+ MessagingCenter.Subscribe(this, "NoteSaved", (sender, savedNote) =>
+ {
+ var existingNote = Notes.FirstOrDefault(n => n.Id == savedNote.Id);
+ if (existingNote != null)
+ {
+ var index = Notes.IndexOf(existingNote);
+ Notes.Remove(existingNote);
+ Notes.Insert(0, savedNote);
+ }
+ else
+ {
+ // В�тавл�ем новую заметку в начало �пи�ка
+ Notes.Insert(0, savedNote);
+ }
+ ApplyCategoryFilter();
+ });
+
+
+ MessagingCenter.Subscribe(this, "NoteDeleted", (sender, deletedNote) =>
+ {
+ Notes.Remove(deletedNote);
+ ApplyCategoryFilter();
+ });
+
+ // Загружаем заметки из базы данных
+ Notes = App.Database.GetAllNotes();
+
+ // Инициализаци� и загрузка данных
+ CategoryManager.LoadCategories();
+ categoryPicker.ItemsSource = CategoryManager.Categories;
+ categoryPicker.SelectedItem = CategoryManager.ActiveCategory ?? CategoryManager.Categories.FirstOrDefault();
+ // Notes = new ObservableCollection();
+ FilteredNotes = new ObservableCollection();
+ BindingContext = this;
+ }
+
+ protected override async void OnAppearing()
+ {
+ base.OnAppearing();
+
+ // Загружаем от�ортированные заметки
+ Notes = new ObservableCollection(App.Database.GetAllNotes().OrderByDescending(n => n.DateCreated));
+
+ // Примен�ем фильтрацию
+ ApplyCategoryFilter();
+
+ // Обновл�ем выбранную категорию в Picker
+ categoryPicker.SelectedItem = CategoryManager.ActiveCategory;
+ }
+
+
+ /* private void OnCollectionViewScrolled(object sender, ItemsViewScrolledEventArgs e)
+ {
+ // Получаем центр �крана по о�и Y
+ double centerScreenY = notesCollectionView.Height / 2;
+
+ // Вы�ота �лемента (подберите в зави�имо�ти от реальной вы�оты �лементов в CollectionView)
+ double itemHeight = 150;
+
+ // Итерируем видимые �лементы по их индек�ам
+ for (int i = e.FirstVisibleItemIndex; i <= e.LastVisibleItemIndex; i++)
+ {
+ // �аходим �лемент � индек�ом i
+ var note = Notes[i]; // предполагаем, что Notes - ObservableCollection
+
+ // Получаем фрейм �лемента через шаблон
+ var item = notesCollectionView.ItemTemplate.CreateContent() as Frame;
+ if (item == null) continue;
+
+ // У�танавливаем BindingContext дл� анимации текущего �лемента
+ item.BindingContext = note;
+
+ // Позици� центра �лемента
+ double itemCenterY = (i * itemHeight) + (itemHeight / 2);
+
+ // Ра�чёт ра��то�ни� до центра �крана
+ double distanceFromCenter = Math.Abs(centerScreenY - itemCenterY);
+
+ // �а�тройка ма�штаба и прозрачно�ти
+ double scale = Math.Max(0.75, 1 - (distanceFromCenter / centerScreenY));
+ double opacity = Math.Max(0.5, 1 - (distanceFromCenter / centerScreenY));
+
+ // �нимации: ма�штабирование и изменение прозрачно�ти
+ item.ScaleTo(scale, 250, Easing.CubicInOut);
+ item.FadeTo(opacity, 250, Easing.CubicInOut);
+ }
+ }*/
+
+
+
+
+ private async void OnNoteTapped(object sender, EventArgs e)
+ {
+ // Получаем контек�т заметки, на которую был выполнен клик
+ var frame = sender as Frame;
+ var selectedNote = frame?.BindingContext as Note;
+
+ if (selectedNote != null)
+ {
+ // Переход на �траницу NoteDetailPage � выбранной заметкой
+ await Navigation.PushAsync(new NoteDetailPage(selectedNote));
+ }
+ }
+
+
+ // Обработчик дл� изменени� тек�та в панели пои�ка
+ private void OnSearchTextChanged(object sender, TextChangedEventArgs e)
+ {
+ ApplySearchFilter(e.NewTextValue);
+ }
+
+ private void ApplySearchFilter(string searchText)
+ {
+ FilteredNotes.Clear();
+ var filteredNotes = Notes
+ .Where(note => string.IsNullOrEmpty(searchText) ||
+ (note.Title?.IndexOf(searchText, StringComparison.OrdinalIgnoreCase) >= 0))
+ .ToList();
+
+ foreach (var note in filteredNotes)
+ {
+ FilteredNotes.Add(note);
+ }
+ }
+
+ private ObservableCollection LoadNotes()
+ {
+ // Зде�ь загружаем заметки из базы данных или локального хранилища
+ return new ObservableCollection();
+ }
+
+ // Обработчик дл� изменени� выбранной категории
+ private void OnCategorySelected(object sender, EventArgs e)
+ {
+ // Провер�ем, что categoryPicker.SelectedItem не �вл�ет�� null
+ if (categoryPicker.SelectedItem == null)
+ return;
+
+ string selectedCategory = categoryPicker.SelectedItem as string;
+
+ // У�танавливаем активную категорию
+ CategoryManager.ActiveCategory = selectedCategory;
+
+ // Провер�ем, что Notes и selectedCategory не null перед фильтрацией
+ if (Notes == null || selectedCategory == null)
+ {
+ FilteredNotes = new ObservableCollection(Notes ?? new ObservableCollection());
+ }
+ else if (selectedCategory == "В�е категории")
+ {
+ FilteredNotes = new ObservableCollection(Notes);
+ }
+ else
+ {
+ FilteredNotes = new ObservableCollection(Notes.Where(note => note.Category == selectedCategory));
+ }
+
+ // Обновл�ем прив�зку данных CollectionView
+ notesCollectionView.ItemsSource = FilteredNotes;
+ }
+
+ protected override void OnDisappearing()
+ {
+ base.OnDisappearing();
+
+ // Отпи�ываем�� от �ообщени� при закрытии �траницы
+ MessagingCenter.Unsubscribe(this, "NoteDeleted");
+ }
+
+ private async void OnNoteSelected(object sender, SelectionChangedEventArgs e)
+ {
+ if (e.CurrentSelection.FirstOrDefault() is Note selectedNote)
+ {
+ // Переход на NoteDetailPage вме�то EditorPage
+ await Navigation.PushAsync(new NoteDetailPage(selectedNote));
+ }
+
+ ((CollectionView)sender).SelectedItem = null;
+ }
+
+
+ /* protected override async void OnAppearing()
+ {
+ base.OnAppearing();
+
+ // Загружаем заметки а�инхронно
+ // Notes = await LoadNotesAsync();
+
+ // Примен�ем фильтрацию на о�нове `ActiveCategory`
+ ApplyCategoryFilter();
+
+ // Обновл�ем `Picker` � категори�ми
+ categoryPicker.SelectedItem = CategoryManager.ActiveCategory;
+ }*/
+
+ private void ApplyCategoryFilter()
+ {
+ if (CategoryManager.ActiveCategory == "В�е категории" || string.IsNullOrEmpty(CategoryManager.ActiveCategory))
+ {
+ FilteredNotes = new ObservableCollection(Notes);
+ }
+ else
+ {
+ // Фильтраци� по категории
+ FilteredNotes = new ObservableCollection(Notes.Where(note => note.Category == CategoryManager.ActiveCategory));
+ }
+
+ // Обновл�ем прив�зку данных дл� CollectionView
+ notesCollectionView.ItemsSource = FilteredNotes;
+ }
+
+
+ private async void AddNoteButton_Clicked(object sender, EventArgs e)
+ {
+ // Создаём новую пу�тую заметку
+ var newNote = new Note();
+
+ // Переход на EditorPage � новой заметкой
+ await Navigation.PushAsync(new EditorPage());
+ }
+
+ }
+
+}
diff --git a/NoteContent/Note.cs b/NoteContent/Note.cs
new file mode 100644
index 0000000..10b976a
--- /dev/null
+++ b/NoteContent/Note.cs
@@ -0,0 +1,65 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Markdig;
+
+
+namespace NotesDataAnalyst.NoteContent
+{
+
+
+ public static class MarkdownExtensions
+ {
+ public static string ToHtml(string markdownText)
+ {
+ // Создание конвейера � необходимыми ра�ширени�ми
+ var pipeline = new MarkdownPipelineBuilder()
+ .UseAdvancedExtensions() // Подключение большин�тва попул�рных ра�ширений
+ .UseEmojiAndSmiley() // Поддержка �модзи и �майлов
+ .UseTaskLists() // Поддержка �пи�ков задач
+ .UseDiagrams() // Поддержка диаграмм
+
+ .Build();
+
+ return Markdown.ToHtml(markdownText, pipeline);
+ }
+ }
+
+
+ public class Note
+ {
+ public string Id { get; set; } = Guid.NewGuid().ToString(); // Уникальный идентификатор дл� заметки
+
+ // Категори� заметки (например, "Личное", "Работа", и т.д.)
+ public string Category { get; set; }
+
+ // Заголовок заметки, и�пользует�� также как ча�ть имени файла
+ public string Title { get; set; }
+
+ // Поле дл� хранени� ��ылки на изображение
+ public string Image { get; set; }
+
+ // О�новной тек�т заметки, который пользователь может вводить в формате Markdown
+ public string Text { get; set; }
+
+ // Свой�тво дл� пути к файлу заметки
+ public string FilePath { get; set; }
+
+ public DateTime DateCreated { get; set; } = DateTime.UtcNow;
+
+ // Свой�тво дл� отображени� тек�та в формате HTML
+ // И�пользуем Markdig дл� преобразовани� тек�та из Markdown в HTML
+ public string FormattedText => MarkdownExtensions.ToHtml(Text);
+
+
+ public string FormattedTitle => MarkdownExtensions.ToHtml(Title);
+
+
+
+
+ }
+
+
+}
diff --git a/NoteContent/NoteDetailPage.xaml b/NoteContent/NoteDetailPage.xaml
new file mode 100644
index 0000000..6beb9cf
--- /dev/null
+++ b/NoteContent/NoteDetailPage.xaml
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/NoteContent/NoteDetailPage.xaml.cs b/NoteContent/NoteDetailPage.xaml.cs
new file mode 100644
index 0000000..aa01e10
--- /dev/null
+++ b/NoteContent/NoteDetailPage.xaml.cs
@@ -0,0 +1,135 @@
+using Markdig.Syntax.Inlines;
+using Markdig;
+using System.Text.RegularExpressions;
+using Markdig.Renderers.Html;
+using Markdig.Syntax;
+using System.Collections.ObjectModel;
+using NotesDataAnalyst.NoteContent;
+
+
+namespace NotesDataAnalyst;
+
+public partial class NoteDetailPage : ContentPage
+{
+ private readonly Note _note;
+
+
+ public NoteDetailPage(Note note)
+ {
+ InitializeComponent();
+ _note = note;
+
+ // Óñòàíàâëèâàåì äàííûå çàìåòêè â ýëåìåíòû óïðàâëåíèÿ
+ titleLabel.Text = _note.Title;
+ //textLabel.Text = _note.Text;
+ BindingContext = _note;
+
+ if (!string.IsNullOrEmpty(_note.Image))
+ {
+ imageView.Source = _note.Image;
+ imageView.IsVisible = true;
+ }
+
+ ApplyThemeToHtml(); // Ïðèìåíÿåì òåìó ê HTML-êîíòåíòó WebView
+ }
+
+ /// //////////////////////////////////// Ðàáîòàåì ñ òåìîé Webwiev
+
+
+ private void ApplyThemeToHtml()
+ {
+ bool isDarkTheme = Application.Current.RequestedTheme == AppTheme.Dark;
+ string backgroundColor = isDarkTheme ? "#212121" : "#FFFFFF";
+ string textColor = isDarkTheme ? "#FFFFFF" : "#000000";
+
+ // Ôîðìèðóåì HTML-êîíòåíò ñ CSS-ñòèëÿìè
+ string htmlContent = $@"
+
+
+
+
+
+ {_note.FormattedText}
+
+ ";
+
+ contentWebView.Source = new HtmlWebViewSource { Html = htmlContent };
+ }
+
+ protected override void OnAppearing()
+ {
+ base.OnAppearing();
+ Application.Current.RequestedThemeChanged += OnRequestedThemeChanged;
+ }
+
+ protected override void OnDisappearing()
+ {
+ base.OnDisappearing();
+ Application.Current.RequestedThemeChanged -= OnRequestedThemeChanged;
+ }
+
+ private void OnRequestedThemeChanged(object sender, AppThemeChangedEventArgs e)
+ {
+ ApplyThemeToHtml(); // Ïåðåçàãðóæàåì HTML ñ íîâîé òåìîé
+ }
+
+/// //////////////////////////////////////////////////////////
+
+
+ // Îáðàáîò÷èê íàâèãàöèè WebView äëÿ ïåðåõâàòà ññûëîê
+ private async void OnWebViewNavigating(object sender, WebNavigatingEventArgs e)
+ {
+ if (e.Url.StartsWith("note:"))
+ {
+ e.Cancel = true; // Îòìåíÿåì ïåðåõîä
+
+ // Èçâëåêàåì Id çàìåòêè èç ññûëêè
+ string noteId = e.Url.Replace("note:", "");
+ await NoteLinkManager.NavigateToNoteByIdAsync(noteId, Navigation);
+ }
+ }
+
+
+
+ // Îáðàáîò÷èê êíîïêè "Ðåäàêòèðîâàòü"
+ private async void OnEditButtonClicked(object sender, EventArgs e)
+ {
+ // Âìåñòî ïåðåäà÷è _note.FilePath ïåðåäàåì ñàì îáúåêò _note
+ await Navigation.PushAsync(new EditorPage(_note));
+ }
+
+ // Îáðàáîò÷èê êíîïêè "Óäàëèòü"
+ private async void OnDeleteButtonClicked(object sender, EventArgs e)
+ {
+ bool confirm = await DisplayAlert("Ïîäòâåðæäåíèå", "Âû óâåðåíû, ÷òî õîòèòå óäàëèòü ýòó çàìåòêó?", "Óäàëèòü", "Îòìåíà");
+ if (confirm)
+ {
+ // Óäàëÿåì ôàéë çàìåòêè
+ // if (File.Exists(_note.FilePath))
+ // { //Óäàëåíèå èç ÁÄ ïîêà íå ðàáîòàåò
+ // await Task.Run (() => App.DbContext.DeleteNote(_note));
+ // File.Delete(_note.FilePath);
+ // Óäàëÿåì çàìåòêó èç áàçû äàííûõ
+ App.Database.DeleteNote(_note);
+ await DisplayAlert("Îïîâåùåíèå", "Ôàéë óäàëåí", "Ok");
+ // Îòïðàâëÿåì ñîîáùåíèå äëÿ îáíîâëåíèÿ ñïèñêà çàìåòîê
+ MessagingCenter.Send(this, "NoteDeleted", _note);
+ // Âîçâðàùàåìñÿ íà ãëàâíóþ ñòðàíèöó è îáíîâëÿåì ñïèñîê
+ await Navigation.PushAsync(new MainPage());
+ // }
+ // else { await DisplayAlert("Îøèáêà", "Çàïèñü íå ìîæåò áûòü óäàëåíà, òàê êàê ôàéë íå íàéäåí", "Ok"); }
+
+
+
+
+ }
+ }
+}
diff --git a/NoteContent/NoteLinkManager.cs b/NoteContent/NoteLinkManager.cs
new file mode 100644
index 0000000..7db5b06
--- /dev/null
+++ b/NoteContent/NoteLinkManager.cs
@@ -0,0 +1,48 @@
+using System.Text.RegularExpressions;
+using Microsoft.Maui.Controls;
+
+namespace NotesDataAnalyst.NoteContent
+{
+ public static class NoteLinkManager
+ {
+ private static readonly Regex LinkPattern = new(@"(.*?)", RegexOptions.Compiled);
+
+ // Метод дл� в�тавки HTML-��ылки на заметку
+ public static string InsertLink(string currentText, Note selectedNote)
+ {
+ string linkText = $"{selectedNote.Title}";
+ return currentText + " " + linkText;
+ }
+
+ // Метод дл� извлечени� ��ылок в HTML
+ public static List<(string Id, string Title)> ParseLinks(string html)
+ {
+ var links = new List<(string Id, string Title)>();
+ var matches = LinkPattern.Matches(html);
+
+ foreach (Match match in matches)
+ {
+ string id = match.Groups[1].Value;
+ string title = match.Groups[2].Value;
+ links.Add((id, title));
+ }
+
+ return links;
+ }
+
+ // Реализуем метод NavigateToNoteByIdAsync дл� перехода к заметке по Id
+ public static async Task NavigateToNoteByIdAsync(string noteId, INavigation navigation)
+ {
+ var note = App.Database.GetAllNotes().FirstOrDefault(n => n.Id == noteId);
+ if (note != null)
+ {
+ await navigation.PushAsync(new NoteDetailPage(note));
+ }
+ else
+ {
+ await Application.Current.MainPage.DisplayAlert("Ошибка", "Заметка не найдена", "OK");
+ }
+ }
+ }
+}
+
diff --git a/NoteContent/NoteSelectionPopup.xaml b/NoteContent/NoteSelectionPopup.xaml
new file mode 100644
index 0000000..e1ae517
--- /dev/null
+++ b/NoteContent/NoteSelectionPopup.xaml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/NoteContent/NoteSelectionPopup.xaml.cs b/NoteContent/NoteSelectionPopup.xaml.cs
new file mode 100644
index 0000000..71970ef
--- /dev/null
+++ b/NoteContent/NoteSelectionPopup.xaml.cs
@@ -0,0 +1,59 @@
+using CommunityToolkit.Maui.Views;
+using NotesDataAnalyst.NoteContent;
+
+
+namespace NotesDataAnalyst
+{
+ public partial class NoteSelectionPopup : Popup
+ {
+ public event Action NoteSelected;
+
+ public NoteSelectionPopup()
+ {
+ InitializeComponent();
+ notesCollectionView.ItemsSource = App.Database.GetAllNotes(); // Çàãðóçêà çàìåòîê èç ÁÄ
+ }
+
+ private void OnSearchTextChanged(object sender, TextChangedEventArgs e)
+ {
+ string searchText = e.NewTextValue.ToLower();
+ notesCollectionView.ItemsSource = App.Database.GetAllNotes()
+ .Where(note => note.Title.ToLower().Contains(searchText));
+ }
+
+ private async void OnNoteSelected(object sender, SelectionChangedEventArgs e)
+ {
+
+
+ if (e.CurrentSelection.FirstOrDefault() is Note selectedNote)
+ {
+
+ NoteSelected?.Invoke(selectedNote);
+ Close(); // Çàêðûâàåì îêíî ïîñëå âûáîðà çàìåòêè
+ }
+ }
+
+ private async void OnNoteTapped(object sender, EventArgs e)
+ {
+ // Ïîëó÷àåì ýëåìåíò, íà êîòîðûé êëèêíóëè
+ var tappedFrame = (Frame)sender;
+ var selectedNote = (Note)tappedFrame.BindingContext;
+
+ if (selectedNote != null)
+ {
+ NoteSelected?.Invoke(selectedNote);
+ Close(); // Çàêðûâàåì îêíî ïîñëå âûáîðà çàìåòêè
+ }
+ }
+
+
+ private void OnCancelButtonClicked(object sender, EventArgs e)
+ {
+ Close(); // Çàêðûòèå îêíà ïî íàæàòèþ íà êíîïêó Îòìåíà
+ }
+
+
+
+
+ }
+}
diff --git a/NotesDataAnalyst.csproj b/NotesDataAnalyst.csproj
new file mode 100644
index 0000000..c37b54a
--- /dev/null
+++ b/NotesDataAnalyst.csproj
@@ -0,0 +1,89 @@
+
+
+
+ net8.0-android;net8.0-ios;net8.0-maccatalyst
+ $(TargetFrameworks);net8.0-windows10.0.19041.0
+
+
+
+
+
+
+ Exe
+ NotesDataAnalyst
+ true
+ true
+ enable
+ enable
+
+
+ NotesDataAnalyst
+
+
+ com.companyname.notesdataanalyst
+
+
+ 1.0
+ 1
+
+ 11.0
+ 13.1
+ 21.0
+ 10.0.17763.0
+ 10.0.17763.0
+ 6.5
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ MSBuild:Compile
+
+
+ MSBuild:Compile
+
+
+ MSBuild:Compile
+
+
+ MSBuild:Compile
+
+
+ MSBuild:Compile
+
+
+ MSBuild:Compile
+
+
+
+
diff --git a/NotesDataAnalyst.sln b/NotesDataAnalyst.sln
new file mode 100644
index 0000000..57fae7f
--- /dev/null
+++ b/NotesDataAnalyst.sln
@@ -0,0 +1,27 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.9.34728.123
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NotesDataAnalyst", "NotesDataAnalyst.csproj", "{8AAA7775-10E2-43A6-ACB4-DDD53C514A9E}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {8AAA7775-10E2-43A6-ACB4-DDD53C514A9E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {8AAA7775-10E2-43A6-ACB4-DDD53C514A9E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {8AAA7775-10E2-43A6-ACB4-DDD53C514A9E}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
+ {8AAA7775-10E2-43A6-ACB4-DDD53C514A9E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {8AAA7775-10E2-43A6-ACB4-DDD53C514A9E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {8AAA7775-10E2-43A6-ACB4-DDD53C514A9E}.Release|Any CPU.Deploy.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {BB40F5E4-7F23-47F8-9832-56F12E8279A1}
+ EndGlobalSection
+EndGlobal
diff --git a/Platforms/Android/AndroidManifest.xml b/Platforms/Android/AndroidManifest.xml
new file mode 100644
index 0000000..e9937ad
--- /dev/null
+++ b/Platforms/Android/AndroidManifest.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Platforms/Android/MainActivity.cs b/Platforms/Android/MainActivity.cs
new file mode 100644
index 0000000..619b577
--- /dev/null
+++ b/Platforms/Android/MainActivity.cs
@@ -0,0 +1,11 @@
+using Android.App;
+using Android.Content.PM;
+using Android.OS;
+
+namespace NotesDataAnalyst
+{
+ [Activity(Theme = "@style/Maui.SplashTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize | ConfigChanges.Density)]
+ public class MainActivity : MauiAppCompatActivity
+ {
+ }
+}
diff --git a/Platforms/Android/MainApplication.cs b/Platforms/Android/MainApplication.cs
new file mode 100644
index 0000000..d5a50bf
--- /dev/null
+++ b/Platforms/Android/MainApplication.cs
@@ -0,0 +1,16 @@
+using Android.App;
+using Android.Runtime;
+
+namespace NotesDataAnalyst
+{
+ [Application]
+ public class MainApplication : MauiApplication
+ {
+ public MainApplication(IntPtr handle, JniHandleOwnership ownership)
+ : base(handle, ownership)
+ {
+ }
+
+ protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
+ }
+}
diff --git a/Platforms/Android/Resources/values/colors.xml b/Platforms/Android/Resources/values/colors.xml
new file mode 100644
index 0000000..c04d749
--- /dev/null
+++ b/Platforms/Android/Resources/values/colors.xml
@@ -0,0 +1,6 @@
+
+
+ #512BD4
+ #2B0B98
+ #2B0B98
+
\ No newline at end of file
diff --git a/Platforms/MacCatalyst/AppDelegate.cs b/Platforms/MacCatalyst/AppDelegate.cs
new file mode 100644
index 0000000..907c3c4
--- /dev/null
+++ b/Platforms/MacCatalyst/AppDelegate.cs
@@ -0,0 +1,10 @@
+using Foundation;
+
+namespace NotesDataAnalyst
+{
+ [Register("AppDelegate")]
+ public class AppDelegate : MauiUIApplicationDelegate
+ {
+ protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
+ }
+}
diff --git a/Platforms/MacCatalyst/Entitlements.plist b/Platforms/MacCatalyst/Entitlements.plist
new file mode 100644
index 0000000..de4adc9
--- /dev/null
+++ b/Platforms/MacCatalyst/Entitlements.plist
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+ com.apple.security.app-sandbox
+
+
+ com.apple.security.network.client
+
+
+
+
diff --git a/Platforms/MacCatalyst/Info.plist b/Platforms/MacCatalyst/Info.plist
new file mode 100644
index 0000000..7268977
--- /dev/null
+++ b/Platforms/MacCatalyst/Info.plist
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ UIDeviceFamily
+
+ 2
+
+ UIRequiredDeviceCapabilities
+
+ arm64
+
+ UISupportedInterfaceOrientations
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UISupportedInterfaceOrientations~ipad
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationPortraitUpsideDown
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ XSAppIconAssets
+ Assets.xcassets/appicon.appiconset
+
+
diff --git a/Platforms/MacCatalyst/Program.cs b/Platforms/MacCatalyst/Program.cs
new file mode 100644
index 0000000..bdd27b2
--- /dev/null
+++ b/Platforms/MacCatalyst/Program.cs
@@ -0,0 +1,16 @@
+using ObjCRuntime;
+using UIKit;
+
+namespace NotesDataAnalyst
+{
+ public class Program
+ {
+ // This is the main entry point of the application.
+ static void Main(string[] args)
+ {
+ // if you want to use a different Application Delegate class from "AppDelegate"
+ // you can specify it here.
+ UIApplication.Main(args, null, typeof(AppDelegate));
+ }
+ }
+}
diff --git a/Platforms/Tizen/Main.cs b/Platforms/Tizen/Main.cs
new file mode 100644
index 0000000..9f37209
--- /dev/null
+++ b/Platforms/Tizen/Main.cs
@@ -0,0 +1,17 @@
+using Microsoft.Maui;
+using Microsoft.Maui.Hosting;
+using System;
+
+namespace NotesDataAnalyst
+{
+ internal class Program : MauiApplication
+ {
+ protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
+
+ static void Main(string[] args)
+ {
+ var app = new Program();
+ app.Run(args);
+ }
+ }
+}
diff --git a/Platforms/Tizen/tizen-manifest.xml b/Platforms/Tizen/tizen-manifest.xml
new file mode 100644
index 0000000..5acab85
--- /dev/null
+++ b/Platforms/Tizen/tizen-manifest.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+ maui-appicon-placeholder
+
+
+
+
+ http://tizen.org/privilege/internet
+
+
+
+
\ No newline at end of file
diff --git a/Platforms/Windows/App.xaml b/Platforms/Windows/App.xaml
new file mode 100644
index 0000000..02129be
--- /dev/null
+++ b/Platforms/Windows/App.xaml
@@ -0,0 +1,8 @@
+
+
+
diff --git a/Platforms/Windows/App.xaml.cs b/Platforms/Windows/App.xaml.cs
new file mode 100644
index 0000000..45e0719
--- /dev/null
+++ b/Platforms/Windows/App.xaml.cs
@@ -0,0 +1,25 @@
+using Microsoft.UI.Xaml;
+
+// To learn more about WinUI, the WinUI project structure,
+// and more about our project templates, see: http://aka.ms/winui-project-info.
+
+namespace NotesDataAnalyst.WinUI
+{
+ ///
+ /// Provides application-specific behavior to supplement the default Application class.
+ ///
+ public partial class App : MauiWinUIApplication
+ {
+ ///
+ /// Initializes the singleton application object. This is the first line of authored code
+ /// executed, and as such is the logical equivalent of main() or WinMain().
+ ///
+ public App()
+ {
+ this.InitializeComponent();
+ }
+
+ protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
+ }
+
+}
diff --git a/Platforms/Windows/Package.appxmanifest b/Platforms/Windows/Package.appxmanifest
new file mode 100644
index 0000000..1cf874e
--- /dev/null
+++ b/Platforms/Windows/Package.appxmanifest
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+ $placeholder$
+ User Name
+ $placeholder$.png
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Platforms/Windows/app.manifest b/Platforms/Windows/app.manifest
new file mode 100644
index 0000000..4e0ae20
--- /dev/null
+++ b/Platforms/Windows/app.manifest
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+ true/PM
+ PerMonitorV2, PerMonitor
+
+
+
diff --git a/Platforms/iOS/AppDelegate.cs b/Platforms/iOS/AppDelegate.cs
new file mode 100644
index 0000000..907c3c4
--- /dev/null
+++ b/Platforms/iOS/AppDelegate.cs
@@ -0,0 +1,10 @@
+using Foundation;
+
+namespace NotesDataAnalyst
+{
+ [Register("AppDelegate")]
+ public class AppDelegate : MauiUIApplicationDelegate
+ {
+ protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
+ }
+}
diff --git a/Platforms/iOS/Info.plist b/Platforms/iOS/Info.plist
new file mode 100644
index 0000000..0004a4f
--- /dev/null
+++ b/Platforms/iOS/Info.plist
@@ -0,0 +1,32 @@
+
+
+
+
+ LSRequiresIPhoneOS
+
+ UIDeviceFamily
+
+ 1
+ 2
+
+ UIRequiredDeviceCapabilities
+
+ arm64
+
+ UISupportedInterfaceOrientations
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UISupportedInterfaceOrientations~ipad
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationPortraitUpsideDown
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ XSAppIconAssets
+ Assets.xcassets/appicon.appiconset
+
+
diff --git a/Platforms/iOS/Program.cs b/Platforms/iOS/Program.cs
new file mode 100644
index 0000000..bdd27b2
--- /dev/null
+++ b/Platforms/iOS/Program.cs
@@ -0,0 +1,16 @@
+using ObjCRuntime;
+using UIKit;
+
+namespace NotesDataAnalyst
+{
+ public class Program
+ {
+ // This is the main entry point of the application.
+ static void Main(string[] args)
+ {
+ // if you want to use a different Application Delegate class from "AppDelegate"
+ // you can specify it here.
+ UIApplication.Main(args, null, typeof(AppDelegate));
+ }
+ }
+}
diff --git a/Properties/launchSettings.json b/Properties/launchSettings.json
new file mode 100644
index 0000000..edf8aad
--- /dev/null
+++ b/Properties/launchSettings.json
@@ -0,0 +1,8 @@
+{
+ "profiles": {
+ "Windows Machine": {
+ "commandName": "MsixPackage",
+ "nativeDebugging": false
+ }
+ }
+}
\ No newline at end of file
diff --git a/Resources/AppIcon/appicon.svg b/Resources/AppIcon/appicon.svg
new file mode 100644
index 0000000..9d63b65
--- /dev/null
+++ b/Resources/AppIcon/appicon.svg
@@ -0,0 +1,4 @@
+
+
\ No newline at end of file
diff --git a/Resources/AppIcon/appiconfg.svg b/Resources/AppIcon/appiconfg.svg
new file mode 100644
index 0000000..21dfb25
--- /dev/null
+++ b/Resources/AppIcon/appiconfg.svg
@@ -0,0 +1,8 @@
+
+
+
\ No newline at end of file
diff --git a/Resources/Fonts/OpenSans-Regular.ttf b/Resources/Fonts/OpenSans-Regular.ttf
new file mode 100644
index 0000000..2d1edf0
Binary files /dev/null and b/Resources/Fonts/OpenSans-Regular.ttf differ
diff --git a/Resources/Fonts/OpenSans-Semibold.ttf b/Resources/Fonts/OpenSans-Semibold.ttf
new file mode 100644
index 0000000..fe13d06
Binary files /dev/null and b/Resources/Fonts/OpenSans-Semibold.ttf differ
diff --git a/Resources/Images/add.png b/Resources/Images/add.png
new file mode 100644
index 0000000..aef0410
Binary files /dev/null and b/Resources/Images/add.png differ
diff --git a/Resources/Images/adda.png b/Resources/Images/adda.png
new file mode 100644
index 0000000..db5b9d2
Binary files /dev/null and b/Resources/Images/adda.png differ
diff --git a/Resources/Images/delete.png b/Resources/Images/delete.png
new file mode 100644
index 0000000..e7bc9a7
Binary files /dev/null and b/Resources/Images/delete.png differ
diff --git a/Resources/Images/dotnet_bot.png b/Resources/Images/dotnet_bot.png
new file mode 100644
index 0000000..f93ce02
Binary files /dev/null and b/Resources/Images/dotnet_bot.png differ
diff --git a/Resources/Images/pencil.png b/Resources/Images/pencil.png
new file mode 100644
index 0000000..f3e57f1
Binary files /dev/null and b/Resources/Images/pencil.png differ
diff --git a/Resources/Images/upload.png b/Resources/Images/upload.png
new file mode 100644
index 0000000..149596d
Binary files /dev/null and b/Resources/Images/upload.png differ
diff --git a/Resources/Images/verified.png b/Resources/Images/verified.png
new file mode 100644
index 0000000..6f7fd56
Binary files /dev/null and b/Resources/Images/verified.png differ
diff --git a/Resources/Images/verifiednight.png b/Resources/Images/verifiednight.png
new file mode 100644
index 0000000..6bfa73d
Binary files /dev/null and b/Resources/Images/verifiednight.png differ
diff --git a/Resources/Raw/AboutAssets.txt b/Resources/Raw/AboutAssets.txt
new file mode 100644
index 0000000..15d6244
--- /dev/null
+++ b/Resources/Raw/AboutAssets.txt
@@ -0,0 +1,15 @@
+Any raw assets you want to be deployed with your application can be placed in
+this directory (and child directories). Deployment of the asset to your application
+is automatically handled by the following `MauiAsset` Build Action within your `.csproj`.
+
+
+
+These files will be deployed with you package and will be accessible using Essentials:
+
+ async Task LoadMauiAsset()
+ {
+ using var stream = await FileSystem.OpenAppPackageFileAsync("AboutAssets.txt");
+ using var reader = new StreamReader(stream);
+
+ var contents = reader.ReadToEnd();
+ }
diff --git a/Resources/Splash/splash.svg b/Resources/Splash/splash.svg
new file mode 100644
index 0000000..21dfb25
--- /dev/null
+++ b/Resources/Splash/splash.svg
@@ -0,0 +1,8 @@
+
+
+
\ No newline at end of file
diff --git a/Resources/Styles/Colors.xaml b/Resources/Styles/Colors.xaml
new file mode 100644
index 0000000..30307a5
--- /dev/null
+++ b/Resources/Styles/Colors.xaml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+ #512BD4
+ #ac99ea
+ #242424
+ #DFD8F7
+ #9880e5
+ #2B0B98
+
+ White
+ Black
+ #D600AA
+ #190649
+ #1f1f1f
+
+ #E1E1E1
+ #C8C8C8
+ #ACACAC
+ #919191
+ #6E6E6E
+ #404040
+ #212121
+ #141414
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Resources/Styles/Styles.xaml b/Resources/Styles/Styles.xaml
new file mode 100644
index 0000000..e0d36bb
--- /dev/null
+++ b/Resources/Styles/Styles.xaml
@@ -0,0 +1,426 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+