defined & added repeat-patterns.
This commit is contained in:
		
							
								
								
									
										10
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								README.md
									
									
									
									
									
								
							@@ -4,12 +4,12 @@ A Reminder based on todo.txt synced via nextcloud
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
## Current todos:
 | 
					## Current todos:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- [ ] make repeat-datatype (like: daily, weekly on mo/th/fr, bi-monthly, etc.)
 | 
					- [x] make repeat-datatype (like: daily, weekly on mo/th/fr, bi-monthly, etc.)
 | 
				
			||||||
  - [ ] define isomorphism for 'repeat:'-meta-tag
 | 
					  - [x] define isomorphism for 'repeat:'-meta-tag
 | 
				
			||||||
- [ ] add interface for repeat-datatype in addReminder.dart
 | 
					- [ ] add interface for repeat-datatype in addReminder.dart
 | 
				
			||||||
- [x] save/load data to/from disk
 | 
					- [x] save/load data to/from disk
 | 
				
			||||||
- [x] adding/removing tasks
 | 
					- [x] adding/removing tasks
 | 
				
			||||||
- [ ] respect ordering that was used when starting the app when saving.
 | 
					- [x] respect ordering that was used when starting the app when saving.
 | 
				
			||||||
- [ ] add Nextcloud-login for getting a Token
 | 
					- [ ] add Nextcloud-login for getting a Token
 | 
				
			||||||
- [ ] use webdav for synchronizing with Nextcloud using that token
 | 
					- [ ] use webdav for synchronizing with Nextcloud using that token
 | 
				
			||||||
- [ ] sorting by "next up", "priority"
 | 
					- [ ] sorting by "next up", "priority"
 | 
				
			||||||
@@ -28,6 +28,10 @@ A Reminder based on todo.txt synced via nextcloud
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
### Adding Tasks
 | 
					### Adding Tasks
 | 
				
			||||||

 | 
					
 | 
				
			||||||
 | 
					(still missing repeat-options, currently defaults to daily.)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Details/Removing tasks
 | 
					### Details/Removing tasks
 | 
				
			||||||

 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Complex repeat patterns
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										
											BIN
										
									
								
								img/2023-01-10_repeat_patterns.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								img/2023-01-10_repeat_patterns.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 44 KiB  | 
@@ -1,5 +1,6 @@
 | 
				
			|||||||
import 'package:flutter/material.dart';
 | 
					import 'package:flutter/material.dart';
 | 
				
			||||||
import 'package:nextcloud_reminder/repeating_task.dart';
 | 
					import 'package:nextcloud_reminder/repeating_task.dart';
 | 
				
			||||||
 | 
					import 'package:nextcloud_reminder/types/repeat.dart';
 | 
				
			||||||
import 'package:nextcloud_reminder/types/tasks.dart';
 | 
					import 'package:nextcloud_reminder/types/tasks.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class AddTaskWidget extends StatefulWidget {
 | 
					class AddTaskWidget extends StatefulWidget {
 | 
				
			||||||
@@ -124,7 +125,7 @@ class _AddTaskWidgetState extends State<AddTaskWidget> with RestorationMixin {
 | 
				
			|||||||
                      const SnackBar(content: Text('Task added.')),
 | 
					                      const SnackBar(content: Text('Task added.')),
 | 
				
			||||||
                    );
 | 
					                    );
 | 
				
			||||||
                    widget.onSave(RepeatingTask(
 | 
					                    widget.onSave(RepeatingTask(
 | 
				
			||||||
                        task: Task(title: _titleController.text, begin: _beginDate.value), repeat: _repeat,));
 | 
					                        task: TaskExtra(title: _titleController.text, begin: _beginDate.value, repeat: [RepeatInterval(interval: DateInterval.daily)],)));
 | 
				
			||||||
                    Navigator.pop(context);
 | 
					                    Navigator.pop(context);
 | 
				
			||||||
                  }
 | 
					                  }
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,6 +8,7 @@ import 'package:nextcloud_reminder/repeating_task.dart';
 | 
				
			|||||||
import 'package:nextcloud_reminder/table.dart';
 | 
					import 'package:nextcloud_reminder/table.dart';
 | 
				
			||||||
import 'package:nextcloud_reminder/types/tasks.dart';
 | 
					import 'package:nextcloud_reminder/types/tasks.dart';
 | 
				
			||||||
import 'package:path_provider/path_provider.dart';
 | 
					import 'package:path_provider/path_provider.dart';
 | 
				
			||||||
 | 
					import 'package:tuple/tuple.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class HomeWidget extends StatefulWidget {
 | 
					class HomeWidget extends StatefulWidget {
 | 
				
			||||||
  const HomeWidget({super.key, required this.title});
 | 
					  const HomeWidget({super.key, required this.title});
 | 
				
			||||||
@@ -30,10 +31,9 @@ class HomeWidget extends StatefulWidget {
 | 
				
			|||||||
class _HomeWidgetState extends State<HomeWidget> with WidgetsBindingObserver {
 | 
					class _HomeWidgetState extends State<HomeWidget> with WidgetsBindingObserver {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  late final ScrollTable _checkboxes;
 | 
					  late final ScrollTable _checkboxes;
 | 
				
			||||||
  int _counter = 1;
 | 
					 | 
				
			||||||
  late final Directory appDocDirectory;
 | 
					  late final Directory appDocDirectory;
 | 
				
			||||||
  late final File todotxt = File('${appDocDirectory.path}/todo.txt');
 | 
					  late final File todotxt = File('${appDocDirectory.path}/todo.txt');
 | 
				
			||||||
  final List<Task> tasks = [];
 | 
					  final List<TaskExtra> tasks = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  void _loadTodos() {
 | 
					  void _loadTodos() {
 | 
				
			||||||
    for (var element in tasks) {
 | 
					    for (var element in tasks) {
 | 
				
			||||||
@@ -81,7 +81,9 @@ class _HomeWidgetState extends State<HomeWidget> with WidgetsBindingObserver {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
  void saveData() async {
 | 
					  void saveData() async {
 | 
				
			||||||
    //TODO: better update lines instead of blindly overwriting.
 | 
					    //TODO: better update lines instead of blindly overwriting.
 | 
				
			||||||
    String data = tasks.map((t) => t.toString()).join("\n");
 | 
					    List<TaskExtra> tmp = tasks;
 | 
				
			||||||
 | 
					    tmp.sort((a, b) => a.lineNumber != null && b.lineNumber != null ? a.lineNumber! - b.lineNumber! : -1,);
 | 
				
			||||||
 | 
					    String data = tmp.map((t) => t.formatAsTask()).join("\n");
 | 
				
			||||||
    debugPrint("Saving:\n$data");
 | 
					    debugPrint("Saving:\n$data");
 | 
				
			||||||
    await todotxt.writeAsString(data);
 | 
					    await todotxt.writeAsString(data);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -90,7 +92,7 @@ class _HomeWidgetState extends State<HomeWidget> with WidgetsBindingObserver {
 | 
				
			|||||||
    if (t != null) {
 | 
					    if (t != null) {
 | 
				
			||||||
      setState(() {
 | 
					      setState(() {
 | 
				
			||||||
        _checkboxes.addTask(t!);
 | 
					        _checkboxes.addTask(t!);
 | 
				
			||||||
        tasks.add(t.task);
 | 
					        tasks.add(TaskExtra.fromTask(t.task));
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,16 +9,18 @@ abstract class TodoParser {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  static final _todoParser = _definition.build();
 | 
					  static final _todoParser = _definition.build();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  static List<Task> parse(List<String> input) {
 | 
					  static List<TaskExtra> parse(List<String> input) {
 | 
				
			||||||
    final List<Task> ret = [];
 | 
					    final List<TaskExtra> ret = [];
 | 
				
			||||||
 | 
					    var line=1;
 | 
				
			||||||
    for (var element in input) {
 | 
					    for (var element in input) {
 | 
				
			||||||
      var parsed = _todoParser.parse(element);
 | 
					      var parsed = _todoParser.parse(element);
 | 
				
			||||||
      if (parsed.isSuccess) {
 | 
					      if (parsed.isSuccess) {
 | 
				
			||||||
        ret.add(parsed.value);
 | 
					        ret.add(TaskExtra.fromTask(parsed.value, lineNumber: line));
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
        debugPrint(parsed.message);
 | 
					        debugPrint(parsed.message);
 | 
				
			||||||
        debugPrint(element);
 | 
					        debugPrint(element);
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					      line++;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return ret;
 | 
					    return ret;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,10 +3,9 @@ import 'package:nextcloud_reminder/task_item.dart';
 | 
				
			|||||||
import 'package:nextcloud_reminder/types/tasks.dart';
 | 
					import 'package:nextcloud_reminder/types/tasks.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class RepeatingTask extends StatefulWidget {
 | 
					class RepeatingTask extends StatefulWidget {
 | 
				
			||||||
  const RepeatingTask({super.key, required this.task, this.repeat=1});
 | 
					  const RepeatingTask({super.key, required this.task});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  final Task task;
 | 
					  final TaskExtra task;
 | 
				
			||||||
  final int repeat;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  State<StatefulWidget> createState() => _RepeatingTaskState();
 | 
					  State<StatefulWidget> createState() => _RepeatingTaskState();
 | 
				
			||||||
@@ -18,7 +17,17 @@ class _RepeatingTaskState extends State<RepeatingTask> {
 | 
				
			|||||||
  @override
 | 
					  @override
 | 
				
			||||||
  void initState() {
 | 
					  void initState() {
 | 
				
			||||||
    super.initState();
 | 
					    super.initState();
 | 
				
			||||||
    _occurrences = List<TaskItem>.generate(10, (index) => TaskItem(done: index % widget.repeat == 0 ? false : null));
 | 
					    _occurrences = List<TaskItem>.generate(10, (index) {
 | 
				
			||||||
 | 
					      var start = widget.task.begin ?? DateTime.now();
 | 
				
			||||||
 | 
					      var comparator = DateTime.now().add(Duration(days: index));
 | 
				
			||||||
 | 
					      for (var r in widget.task.repeat) {
 | 
				
			||||||
 | 
					        if (r.repeatHit(start, comparator)) return TaskItem(done: widget.task.done,);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      if (widget.task.repeat.isEmpty && start.day == comparator.day && start.month == comparator.month && start.year == comparator.year) {
 | 
				
			||||||
 | 
					        return TaskItem(done: widget.task.done,);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      return const TaskItem(done: null);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										97
									
								
								lib/types/repeat.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								lib/types/repeat.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,97 @@
 | 
				
			|||||||
 | 
					class RepeatInterval {
 | 
				
			||||||
 | 
					  int every;
 | 
				
			||||||
 | 
					  DateInterval interval;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  RepeatInterval({required this.interval, this.every=1});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  static RepeatInterval? fromString(String input) {
 | 
				
			||||||
 | 
					    RegExpMatch? m = RegExp(r'(\d*)(\D+)').firstMatch(input);
 | 
				
			||||||
 | 
					    if (m != null) {
 | 
				
			||||||
 | 
					      DateInterval? di = _stringToInterval(m!.group(2)!);
 | 
				
			||||||
 | 
					      if (di != null) {
 | 
				
			||||||
 | 
					        return RepeatInterval(interval: di, every: m?.group(1) == "" ? 1 : int.parse(m!.group(1)!));
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return null;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// Does the RepeatInterval referencing from a hit b?
 | 
				
			||||||
 | 
					  bool repeatHit(DateTime a, DateTime b) {
 | 
				
			||||||
 | 
					    var daydiff = a.difference(b).inDays;
 | 
				
			||||||
 | 
					    var weeks = (daydiff / 7).floor();
 | 
				
			||||||
 | 
					    switch (interval) {
 | 
				
			||||||
 | 
					      case DateInterval.daily:       return daydiff % every == 0;
 | 
				
			||||||
 | 
					      case DateInterval.weekly:      return daydiff % (every*7) == 0;
 | 
				
			||||||
 | 
					      case DateInterval.monthly:     return a.day == b.day;
 | 
				
			||||||
 | 
					      case DateInterval.monday:      return weeks % every == 0 && b.weekday == DateTime.monday;
 | 
				
			||||||
 | 
					      case DateInterval.tuesday:     return weeks % every == 0 && b.weekday == DateTime.tuesday;
 | 
				
			||||||
 | 
					      case DateInterval.wednesday:   return weeks % every == 0 && b.weekday == DateTime.wednesday;
 | 
				
			||||||
 | 
					      case DateInterval.thursday:    return weeks % every == 0 && b.weekday == DateTime.thursday;
 | 
				
			||||||
 | 
					      case DateInterval.friday:      return weeks % every == 0 && b.weekday == DateTime.friday;
 | 
				
			||||||
 | 
					      case DateInterval.saturday:    return weeks % every == 0 && b.weekday == DateTime.saturday;
 | 
				
			||||||
 | 
					      case DateInterval.sunday:      return weeks % every == 0 && b.weekday == DateTime.sunday;
 | 
				
			||||||
 | 
					      case DateInterval.dayOfMonth:  return b.day == every;
 | 
				
			||||||
 | 
					      case DateInterval.dayOfYear:   return DateTime.now().difference(b).inDays == every;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @override
 | 
				
			||||||
 | 
					  toString() {
 | 
				
			||||||
 | 
					    return "$every${_intervalToString(interval)}";
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  static DateInterval? _stringToInterval(String input) {
 | 
				
			||||||
 | 
					    switch (input) {
 | 
				
			||||||
 | 
					      case "daily":
 | 
				
			||||||
 | 
					      case "day": return DateInterval.daily;
 | 
				
			||||||
 | 
					      case "weekly":
 | 
				
			||||||
 | 
					      case "week": return DateInterval.weekly;
 | 
				
			||||||
 | 
					      case "monthly":
 | 
				
			||||||
 | 
					      case "month": return DateInterval.monthly;
 | 
				
			||||||
 | 
					      case "mon": return DateInterval.monday;
 | 
				
			||||||
 | 
					      case "tue": return DateInterval.tuesday;
 | 
				
			||||||
 | 
					      case "wed": return DateInterval.wednesday;
 | 
				
			||||||
 | 
					      case "thu": return DateInterval.thursday;
 | 
				
			||||||
 | 
					      case "fri": return DateInterval.friday;
 | 
				
			||||||
 | 
					      case "sat": return DateInterval.saturday;
 | 
				
			||||||
 | 
					      case "sun": return DateInterval.sunday;
 | 
				
			||||||
 | 
					      case "ofMonth": return DateInterval.dayOfMonth;
 | 
				
			||||||
 | 
					      case "ofYear": return DateInterval.dayOfYear;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return null;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  static String _intervalToString(DateInterval di) {
 | 
				
			||||||
 | 
					    switch (di) {
 | 
				
			||||||
 | 
					      case DateInterval.daily: return "day";
 | 
				
			||||||
 | 
					      case DateInterval.weekly: return "week";
 | 
				
			||||||
 | 
					      case DateInterval.monthly: return "month";
 | 
				
			||||||
 | 
					      case DateInterval.monday: return "mon";
 | 
				
			||||||
 | 
					      case DateInterval.tuesday: return "tue";
 | 
				
			||||||
 | 
					      case DateInterval.wednesday: return "wed";
 | 
				
			||||||
 | 
					      case DateInterval.thursday: return "thu";
 | 
				
			||||||
 | 
					      case DateInterval.friday: return "fri";
 | 
				
			||||||
 | 
					      case DateInterval.saturday: return "sat";
 | 
				
			||||||
 | 
					      case DateInterval.sunday: return "sun";
 | 
				
			||||||
 | 
					      case DateInterval.dayOfMonth: return "ofMonth";
 | 
				
			||||||
 | 
					      case DateInterval.dayOfYear: return "ofYear";
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum DateInterval {
 | 
				
			||||||
 | 
					  daily,
 | 
				
			||||||
 | 
					  weekly,
 | 
				
			||||||
 | 
					  monthly,
 | 
				
			||||||
 | 
					  monday,
 | 
				
			||||||
 | 
					  tuesday,
 | 
				
			||||||
 | 
					  wednesday,
 | 
				
			||||||
 | 
					  thursday,
 | 
				
			||||||
 | 
					  friday,
 | 
				
			||||||
 | 
					  saturday,
 | 
				
			||||||
 | 
					  sunday,
 | 
				
			||||||
 | 
					  dayOfMonth,
 | 
				
			||||||
 | 
					  dayOfYear,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1,3 +1,45 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					import 'package:collection/collection.dart';
 | 
				
			||||||
 | 
					import 'package:nextcloud_reminder/types/repeat.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TaskExtra extends Task {
 | 
				
			||||||
 | 
					  TaskExtra({required super.title,
 | 
				
			||||||
 | 
					    super.done = false,
 | 
				
			||||||
 | 
					    super.contexts = const [],
 | 
				
			||||||
 | 
					    super.projects = const [],
 | 
				
			||||||
 | 
					    super.meta = const {},
 | 
				
			||||||
 | 
					    super.priority,
 | 
				
			||||||
 | 
					    super.begin,
 | 
				
			||||||
 | 
					    super.end,
 | 
				
			||||||
 | 
					    this.lineNumber,
 | 
				
			||||||
 | 
					    this.repeat = const [],
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  TaskExtra.fromTask(Task t, {this.lineNumber})
 | 
				
			||||||
 | 
					      : repeat = t.meta["repeat"]?.split("/").map(RepeatInterval.fromString).whereNotNull().toList() ?? []
 | 
				
			||||||
 | 
					      , super(title: t.title,
 | 
				
			||||||
 | 
					              done: t.done,
 | 
				
			||||||
 | 
					              contexts: t.contexts,
 | 
				
			||||||
 | 
					              projects: t.projects,
 | 
				
			||||||
 | 
					              meta: t.meta,
 | 
				
			||||||
 | 
					              priority: t.priority,
 | 
				
			||||||
 | 
					              begin: t.begin,
 | 
				
			||||||
 | 
					              end: t.end,
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  final int? lineNumber;
 | 
				
			||||||
 | 
					  final List<RepeatInterval> repeat;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  formatAsTask() {
 | 
				
			||||||
 | 
					    return super.toString();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @override
 | 
				
			||||||
 | 
					  String toString() {
 | 
				
			||||||
 | 
					    return "$lineNumber: ${super.toString()}";
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Task {
 | 
					class Task {
 | 
				
			||||||
  final bool done;
 | 
					  final bool done;
 | 
				
			||||||
  final DateTime? begin;
 | 
					  final DateTime? begin;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -30,7 +30,7 @@ packages:
 | 
				
			|||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "1.1.1"
 | 
					    version: "1.1.1"
 | 
				
			||||||
  collection:
 | 
					  collection:
 | 
				
			||||||
    dependency: transitive
 | 
					    dependency: "direct main"
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
      name: collection
 | 
					      name: collection
 | 
				
			||||||
      url: "https://pub.dartlang.org"
 | 
					      url: "https://pub.dartlang.org"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -40,6 +40,7 @@ dependencies:
 | 
				
			|||||||
  petitparser: ^5.1.0
 | 
					  petitparser: ^5.1.0
 | 
				
			||||||
  tuple: ^2.0.1
 | 
					  tuple: ^2.0.1
 | 
				
			||||||
  flutter_window_close: ^0.2.2
 | 
					  flutter_window_close: ^0.2.2
 | 
				
			||||||
 | 
					  collection: ^1.16.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
dev_dependencies:
 | 
					dev_dependencies:
 | 
				
			||||||
  flutter_test:
 | 
					  flutter_test:
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user