update icons and tab order
This commit is contained in:
Binary file not shown.
Symlink
+1
@@ -0,0 +1 @@
|
|||||||
|
../../../font/src/assets/generated-icons/WebLibre.ttf
|
||||||
@@ -23,7 +23,8 @@ import 'package:weblibre/utils/uri_policy.dart';
|
|||||||
extension UriX on Uri {
|
extension UriX on Uri {
|
||||||
Uri get base => Uri.parse('$scheme://$authority');
|
Uri get base => Uri.parse('$scheme://$authority');
|
||||||
|
|
||||||
bool get hasSupportedScheme => allSupportedSchemes.any((s) => s.name == scheme);
|
bool get hasSupportedScheme =>
|
||||||
|
allSupportedSchemes.any((s) => s.name == scheme);
|
||||||
|
|
||||||
bool get isHttp => isScheme('http');
|
bool get isHttp => isScheme('http');
|
||||||
bool get isHttps => isScheme('https');
|
bool get isHttps => isScheme('https');
|
||||||
|
|||||||
+8
-8
@@ -152,13 +152,6 @@ class AppBarTitle extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
if (isTabTuneledAsync.hasValue &&
|
|
||||||
isTabTuneledAsync.value == true) ...[
|
|
||||||
const Icon(MdiIcons.tunnelOutline, size: 14),
|
|
||||||
const SizedBox(width: 4),
|
|
||||||
],
|
|
||||||
icon,
|
|
||||||
const SizedBox(width: 4),
|
|
||||||
if (tabState.tabMode is PrivateTabMode) ...[
|
if (tabState.tabMode is PrivateTabMode) ...[
|
||||||
Icon(
|
Icon(
|
||||||
MdiIcons.dominoMask,
|
MdiIcons.dominoMask,
|
||||||
@@ -168,12 +161,19 @@ class AppBarTitle extends HookConsumerWidget {
|
|||||||
const SizedBox(width: 4),
|
const SizedBox(width: 4),
|
||||||
] else if (tabState.tabMode is IsolatedTabMode) ...[
|
] else if (tabState.tabMode is IsolatedTabMode) ...[
|
||||||
Icon(
|
Icon(
|
||||||
MdiIcons.shieldLock,
|
MdiIcons.snowflake,
|
||||||
color: appColors.isolatedTabTeal,
|
color: appColors.isolatedTabTeal,
|
||||||
size: 14,
|
size: 14,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 4),
|
const SizedBox(width: 4),
|
||||||
],
|
],
|
||||||
|
if (isTabTuneledAsync.hasValue &&
|
||||||
|
isTabTuneledAsync.value == true) ...[
|
||||||
|
const Icon(MdiIcons.tunnelOutline, size: 14),
|
||||||
|
const SizedBox(width: 4),
|
||||||
|
],
|
||||||
|
icon,
|
||||||
|
const SizedBox(width: 4),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: UriBreadcrumb(
|
child: UriBreadcrumb(
|
||||||
uri: tabState.url,
|
uri: tabState.url,
|
||||||
|
|||||||
+1
-1
@@ -553,7 +553,7 @@ class QuickTabSwitcher extends HookConsumerWidget {
|
|||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(left: 8.0),
|
padding: const EdgeInsets.only(left: 8.0),
|
||||||
child: Icon(
|
child: Icon(
|
||||||
MdiIcons.shieldLock,
|
MdiIcons.snowflake,
|
||||||
color: appColors.isolatedTabTeal,
|
color: appColors.isolatedTabTeal,
|
||||||
size: 20,
|
size: 20,
|
||||||
),
|
),
|
||||||
|
|||||||
+21
-19
@@ -23,7 +23,6 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|||||||
import 'package:weblibre/core/design/app_colors.dart';
|
import 'package:weblibre/core/design/app_colors.dart';
|
||||||
import 'package:weblibre/core/routing/routes.dart';
|
import 'package:weblibre/core/routing/routes.dart';
|
||||||
import 'package:weblibre/features/user/domain/repositories/general_settings.dart';
|
import 'package:weblibre/features/user/domain/repositories/general_settings.dart';
|
||||||
import 'package:weblibre/presentation/icons/weblibre_icons.dart';
|
|
||||||
|
|
||||||
class TabCreationMenu extends HookConsumerWidget {
|
class TabCreationMenu extends HookConsumerWidget {
|
||||||
final Widget child;
|
final Widget child;
|
||||||
@@ -61,24 +60,6 @@ class TabCreationMenu extends HookConsumerWidget {
|
|||||||
leadingIcon: const Icon(MdiIcons.tab),
|
leadingIcon: const Icon(MdiIcons.tab),
|
||||||
child: const Text('Add Regular Tab'),
|
child: const Text('Add Regular Tab'),
|
||||||
),
|
),
|
||||||
MenuItemButton(
|
|
||||||
onPressed: () async {
|
|
||||||
await const SearchRoute(tabType: TabType.private).push(context);
|
|
||||||
},
|
|
||||||
leadingIcon: const Icon(WebLibreIcons.privateTab),
|
|
||||||
child: const Text('Add Private Tab'),
|
|
||||||
),
|
|
||||||
if (showIsolatedTabUi)
|
|
||||||
MenuItemButton(
|
|
||||||
onPressed: () async {
|
|
||||||
await const SearchRoute(tabType: TabType.isolated).push(context);
|
|
||||||
},
|
|
||||||
leadingIcon: Icon(
|
|
||||||
MdiIcons.shieldLock,
|
|
||||||
color: AppColors.of(context).isolatedTabTeal,
|
|
||||||
),
|
|
||||||
child: const Text('Add Isolated Tab'),
|
|
||||||
),
|
|
||||||
if (createChildTabsOption)
|
if (createChildTabsOption)
|
||||||
MenuItemButton(
|
MenuItemButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
@@ -87,6 +68,27 @@ class TabCreationMenu extends HookConsumerWidget {
|
|||||||
leadingIcon: const Icon(MdiIcons.fileTree),
|
leadingIcon: const Icon(MdiIcons.fileTree),
|
||||||
child: const Text('Add Child Tab'),
|
child: const Text('Add Child Tab'),
|
||||||
),
|
),
|
||||||
|
MenuItemButton(
|
||||||
|
onPressed: () async {
|
||||||
|
await const SearchRoute(tabType: TabType.private).push(context);
|
||||||
|
},
|
||||||
|
leadingIcon: Icon(
|
||||||
|
MdiIcons.dominoMask,
|
||||||
|
color: AppColors.of(context).privateTabPurple,
|
||||||
|
),
|
||||||
|
child: const Text('Add Private Tab'),
|
||||||
|
),
|
||||||
|
if (showIsolatedTabUi)
|
||||||
|
MenuItemButton(
|
||||||
|
onPressed: () async {
|
||||||
|
await const SearchRoute(tabType: TabType.isolated).push(context);
|
||||||
|
},
|
||||||
|
leadingIcon: Icon(
|
||||||
|
MdiIcons.snowflake,
|
||||||
|
color: AppColors.of(context).isolatedTabTeal,
|
||||||
|
),
|
||||||
|
child: const Text('Add Isolated Tab'),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
child: child,
|
child: child,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -50,7 +50,6 @@ import 'package:weblibre/features/geckoview/features/tabs/domain/repositories/co
|
|||||||
import 'package:weblibre/features/geckoview/features/tabs/domain/repositories/tab.dart';
|
import 'package:weblibre/features/geckoview/features/tabs/domain/repositories/tab.dart';
|
||||||
import 'package:weblibre/features/user/domain/repositories/general_settings.dart';
|
import 'package:weblibre/features/user/domain/repositories/general_settings.dart';
|
||||||
import 'package:weblibre/presentation/hooks/menu_controller.dart';
|
import 'package:weblibre/presentation/hooks/menu_controller.dart';
|
||||||
import 'package:weblibre/presentation/icons/weblibre_icons.dart';
|
|
||||||
import 'package:weblibre/presentation/widgets/website_feed_menu_button.dart';
|
import 'package:weblibre/presentation/widgets/website_feed_menu_button.dart';
|
||||||
import 'package:weblibre/utils/ui_helper.dart' as ui_helper;
|
import 'package:weblibre/utils/ui_helper.dart' as ui_helper;
|
||||||
|
|
||||||
@@ -273,7 +272,10 @@ class TabMenu extends HookConsumerWidget {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
MenuItemButton(
|
MenuItemButton(
|
||||||
leadingIcon: const Icon(WebLibreIcons.privateTab),
|
leadingIcon: Icon(
|
||||||
|
MdiIcons.dominoMask,
|
||||||
|
color: AppColors.of(context).privateTabPurple,
|
||||||
|
),
|
||||||
child: const Text('Private'),
|
child: const Text('Private'),
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
final tabState = ref.read(tabStateProvider(selectedTabId))!;
|
final tabState = ref.read(tabStateProvider(selectedTabId))!;
|
||||||
@@ -318,7 +320,7 @@ class TabMenu extends HookConsumerWidget {
|
|||||||
if (settings.showIsolatedTabUi)
|
if (settings.showIsolatedTabUi)
|
||||||
MenuItemButton(
|
MenuItemButton(
|
||||||
leadingIcon: Icon(
|
leadingIcon: Icon(
|
||||||
MdiIcons.shieldLock,
|
MdiIcons.snowflake,
|
||||||
color: AppColors.of(context).isolatedTabTeal,
|
color: AppColors.of(context).isolatedTabTeal,
|
||||||
),
|
),
|
||||||
child: const Text('Isolated'),
|
child: const Text('Isolated'),
|
||||||
|
|||||||
+2
-2
@@ -233,7 +233,7 @@ class GridTabPreview extends HookConsumerWidget {
|
|||||||
top: -4,
|
top: -4,
|
||||||
child: Icon(
|
child: Icon(
|
||||||
tabState.tabMode is IsolatedTabMode
|
tabState.tabMode is IsolatedTabMode
|
||||||
? MdiIcons.shieldLock
|
? MdiIcons.snowflake
|
||||||
: MdiIcons.dominoMask,
|
: MdiIcons.dominoMask,
|
||||||
color: tabState.tabMode is IsolatedTabMode
|
color: tabState.tabMode is IsolatedTabMode
|
||||||
? appColors.isolatedTabTeal
|
? appColors.isolatedTabTeal
|
||||||
@@ -337,7 +337,7 @@ class ListTabPreview extends HookConsumerWidget {
|
|||||||
};
|
};
|
||||||
final (modeBadgeIcon, modeBadgeColor) = switch (tabState.tabMode) {
|
final (modeBadgeIcon, modeBadgeColor) = switch (tabState.tabMode) {
|
||||||
PrivateTabMode() => (MdiIcons.dominoMask, appColors.privateTabPurple),
|
PrivateTabMode() => (MdiIcons.dominoMask, appColors.privateTabPurple),
|
||||||
IsolatedTabMode() => (MdiIcons.shieldLock, appColors.isolatedTabTeal),
|
IsolatedTabMode() => (MdiIcons.snowflake, appColors.isolatedTabTeal),
|
||||||
RegularTabMode() => (null, null),
|
RegularTabMode() => (null, null),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -516,7 +516,7 @@ class TabViewHeader extends HookConsumerWidget {
|
|||||||
if (showIsolatedTabUi)
|
if (showIsolatedTabUi)
|
||||||
MenuItemButton(
|
MenuItemButton(
|
||||||
leadingIcon: Icon(
|
leadingIcon: Icon(
|
||||||
MdiIcons.shieldLock,
|
MdiIcons.snowflake,
|
||||||
color: AppColors.of(context).isolatedTabTeal,
|
color: AppColors.of(context).isolatedTabTeal,
|
||||||
),
|
),
|
||||||
onPressed: isSyncedScope
|
onPressed: isSyncedScope
|
||||||
|
|||||||
+3
-2
@@ -42,6 +42,7 @@ import 'package:weblibre/features/geckoview/features/tabs/presentation/widgets/c
|
|||||||
import 'package:weblibre/features/user/domain/repositories/general_settings.dart';
|
import 'package:weblibre/features/user/domain/repositories/general_settings.dart';
|
||||||
import 'package:weblibre/presentation/hooks/cached_future.dart';
|
import 'package:weblibre/presentation/hooks/cached_future.dart';
|
||||||
import 'package:weblibre/presentation/hooks/debouncer.dart';
|
import 'package:weblibre/presentation/hooks/debouncer.dart';
|
||||||
|
import 'package:weblibre/presentation/icons/weblibre_icons.dart';
|
||||||
import 'package:weblibre/utils/form_validators.dart';
|
import 'package:weblibre/utils/form_validators.dart';
|
||||||
import 'package:weblibre/utils/ui_helper.dart';
|
import 'package:weblibre/utils/ui_helper.dart';
|
||||||
|
|
||||||
@@ -381,7 +382,7 @@ class OpenSharedContent extends HookConsumerWidget {
|
|||||||
subtitle: 'Add to your browser tabs',
|
subtitle: 'Add to your browser tabs',
|
||||||
icon: MdiIcons.tab,
|
icon: MdiIcons.tab,
|
||||||
trailing: PopupMenuButton<TabMode>(
|
trailing: PopupMenuButton<TabMode>(
|
||||||
icon: const Icon(MdiIcons.tabUnselected, size: 24),
|
icon: const Icon(WebLibreIcons.tabType, size: 24),
|
||||||
tooltip: settings.showIsolatedTabUi
|
tooltip: settings.showIsolatedTabUi
|
||||||
? 'Private / Isolated'
|
? 'Private / Isolated'
|
||||||
: 'Private',
|
: 'Private',
|
||||||
@@ -407,7 +408,7 @@ class OpenSharedContent extends HookConsumerWidget {
|
|||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Icon(
|
Icon(
|
||||||
MdiIcons.shieldLock,
|
MdiIcons.snowflake,
|
||||||
color: appColors.isolatedTabTeal,
|
color: appColors.isolatedTabTeal,
|
||||||
size: 20,
|
size: 20,
|
||||||
),
|
),
|
||||||
|
|||||||
+20
-21
@@ -20,7 +20,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_material_design_icons/flutter_material_design_icons.dart';
|
import 'package:flutter_material_design_icons/flutter_material_design_icons.dart';
|
||||||
import 'package:weblibre/core/routing/routes.dart';
|
import 'package:weblibre/core/routing/routes.dart';
|
||||||
import 'package:weblibre/presentation/icons/weblibre_icons.dart';
|
|
||||||
|
|
||||||
/// An animated tab type switcher that only shows the label for the currently
|
/// An animated tab type switcher that only shows the label for the currently
|
||||||
/// active option. Inactive options collapse to show only their icon.
|
/// active option. Inactive options collapse to show only their icon.
|
||||||
@@ -65,26 +64,6 @@ class AnimatedTabTypeSwitcher extends StatelessWidget {
|
|||||||
selectedBackgroundColor: selectedBackgroundColor,
|
selectedBackgroundColor: selectedBackgroundColor,
|
||||||
onTap: () => onChanged(TabType.regular),
|
onTap: () => onChanged(TabType.regular),
|
||||||
),
|
),
|
||||||
_divider(borderColor),
|
|
||||||
_Segment(
|
|
||||||
tabType: TabType.private,
|
|
||||||
icon: WebLibreIcons.privateTab,
|
|
||||||
label: 'Private',
|
|
||||||
isSelected: selected == TabType.private,
|
|
||||||
selectedBackgroundColor: selectedBackgroundColor,
|
|
||||||
onTap: () => onChanged(TabType.private),
|
|
||||||
),
|
|
||||||
if (showIsolatedOption) ...[
|
|
||||||
_divider(borderColor),
|
|
||||||
_Segment(
|
|
||||||
tabType: TabType.isolated,
|
|
||||||
icon: MdiIcons.shieldLock,
|
|
||||||
label: 'Isolated',
|
|
||||||
isSelected: selected == TabType.isolated,
|
|
||||||
selectedBackgroundColor: selectedBackgroundColor,
|
|
||||||
onTap: () => onChanged(TabType.isolated),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
if (showChildOption) ...[
|
if (showChildOption) ...[
|
||||||
_divider(borderColor),
|
_divider(borderColor),
|
||||||
_Segment(
|
_Segment(
|
||||||
@@ -96,6 +75,26 @@ class AnimatedTabTypeSwitcher extends StatelessWidget {
|
|||||||
onTap: () => onChanged(TabType.child),
|
onTap: () => onChanged(TabType.child),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
_divider(borderColor),
|
||||||
|
_Segment(
|
||||||
|
tabType: TabType.private,
|
||||||
|
icon: MdiIcons.dominoMask,
|
||||||
|
label: 'Private',
|
||||||
|
isSelected: selected == TabType.private,
|
||||||
|
selectedBackgroundColor: selectedBackgroundColor,
|
||||||
|
onTap: () => onChanged(TabType.private),
|
||||||
|
),
|
||||||
|
if (showIsolatedOption) ...[
|
||||||
|
_divider(borderColor),
|
||||||
|
_Segment(
|
||||||
|
tabType: TabType.isolated,
|
||||||
|
icon: MdiIcons.snowflake,
|
||||||
|
label: 'Isolated',
|
||||||
|
isSelected: selected == TabType.isolated,
|
||||||
|
selectedBackgroundColor: selectedBackgroundColor,
|
||||||
|
onTap: () => onChanged(TabType.isolated),
|
||||||
|
),
|
||||||
|
],
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ import 'package:weblibre/features/settings/presentation/controllers/save_setting
|
|||||||
import 'package:weblibre/features/settings/presentation/widgets/sections.dart';
|
import 'package:weblibre/features/settings/presentation/widgets/sections.dart';
|
||||||
import 'package:weblibre/features/user/data/models/general_settings.dart';
|
import 'package:weblibre/features/user/data/models/general_settings.dart';
|
||||||
import 'package:weblibre/features/user/domain/repositories/general_settings.dart';
|
import 'package:weblibre/features/user/domain/repositories/general_settings.dart';
|
||||||
import 'package:weblibre/presentation/icons/weblibre_icons.dart';
|
|
||||||
|
|
||||||
class TabsBehaviorSettingsScreen extends StatelessWidget {
|
class TabsBehaviorSettingsScreen extends StatelessWidget {
|
||||||
const TabsBehaviorSettingsScreen({super.key});
|
const TabsBehaviorSettingsScreen({super.key});
|
||||||
@@ -137,17 +136,22 @@ class _NewTabDefaultSection extends HookConsumerWidget {
|
|||||||
label: Text('Regular'),
|
label: Text('Regular'),
|
||||||
icon: Icon(MdiIcons.tab),
|
icon: Icon(MdiIcons.tab),
|
||||||
),
|
),
|
||||||
const ButtonSegment(
|
ButtonSegment(
|
||||||
value: TabType.private,
|
value: TabType.private,
|
||||||
label: Text('Private'),
|
label: const Text('Private'),
|
||||||
icon: Icon(WebLibreIcons.privateTab),
|
icon: Icon(
|
||||||
|
MdiIcons.dominoMask,
|
||||||
|
color: defaultCreateTabType == TabType.private
|
||||||
|
? null
|
||||||
|
: appColors.privateTabPurple,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
if (settings.showIsolatedTabUi)
|
if (settings.showIsolatedTabUi)
|
||||||
ButtonSegment(
|
ButtonSegment(
|
||||||
value: TabType.isolated,
|
value: TabType.isolated,
|
||||||
label: const Text('Isolated'),
|
label: const Text('Isolated'),
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
MdiIcons.shieldLock,
|
MdiIcons.snowflake,
|
||||||
color: defaultCreateTabType == TabType.isolated
|
color: defaultCreateTabType == TabType.isolated
|
||||||
? null
|
? null
|
||||||
: appColors.isolatedTabTeal,
|
: appColors.isolatedTabTeal,
|
||||||
@@ -206,21 +210,26 @@ class _ExternalLinkHandlingSection extends HookConsumerWidget {
|
|||||||
Center(
|
Center(
|
||||||
child: SegmentedButton(
|
child: SegmentedButton(
|
||||||
showSelectedIcon: false,
|
showSelectedIcon: false,
|
||||||
segments: const [
|
segments: [
|
||||||
ButtonSegment(
|
const ButtonSegment(
|
||||||
value: TabIntentOpenSetting.ask,
|
value: TabIntentOpenSetting.ask,
|
||||||
label: Text('Prompt'),
|
label: Text('Prompt'),
|
||||||
icon: Icon(MdiIcons.messageQuestion),
|
icon: Icon(MdiIcons.messageQuestion),
|
||||||
),
|
),
|
||||||
ButtonSegment(
|
const ButtonSegment(
|
||||||
value: TabIntentOpenSetting.regular,
|
value: TabIntentOpenSetting.regular,
|
||||||
label: Text('Regular'),
|
label: Text('Regular'),
|
||||||
icon: Icon(MdiIcons.tab),
|
icon: Icon(MdiIcons.tab),
|
||||||
),
|
),
|
||||||
ButtonSegment(
|
ButtonSegment(
|
||||||
value: TabIntentOpenSetting.private,
|
value: TabIntentOpenSetting.private,
|
||||||
label: Text('Private'),
|
label: const Text('Private'),
|
||||||
icon: Icon(WebLibreIcons.privateTab),
|
icon: Icon(
|
||||||
|
MdiIcons.dominoMask,
|
||||||
|
color: tabIntentOpenSetting == TabIntentOpenSetting.private
|
||||||
|
? null
|
||||||
|
: appColors.privateTabPurple,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
selected: {tabIntentOpenSetting},
|
selected: {tabIntentOpenSetting},
|
||||||
@@ -326,7 +335,10 @@ class _ShowIsolatedTabUiTile extends HookConsumerWidget {
|
|||||||
return SwitchListTile.adaptive(
|
return SwitchListTile.adaptive(
|
||||||
title: const Text('Show Isolated Tab UI'),
|
title: const Text('Show Isolated Tab UI'),
|
||||||
subtitle: const Text('Show isolated-tab creation options in the UI'),
|
subtitle: const Text('Show isolated-tab creation options in the UI'),
|
||||||
secondary: const Icon(MdiIcons.shieldLock),
|
secondary: Icon(
|
||||||
|
MdiIcons.snowflake,
|
||||||
|
color: AppColors.of(context).isolatedTabTeal,
|
||||||
|
),
|
||||||
value: showIsolatedTabUi,
|
value: showIsolatedTabUi,
|
||||||
onChanged: (value) async {
|
onChanged: (value) async {
|
||||||
await ref.read(saveGeneralSettingsControllerProvider.notifier).save((
|
await ref.read(saveGeneralSettingsControllerProvider.notifier).save((
|
||||||
|
|||||||
@@ -45,7 +45,9 @@ const allSupportedSchemes = [
|
|||||||
const httpOnlySchemes = [kSchemeHttp, kSchemeHttps];
|
const httpOnlySchemes = [kSchemeHttp, kSchemeHttps];
|
||||||
|
|
||||||
bool schemeRequiresAuthority(String scheme) {
|
bool schemeRequiresAuthority(String scheme) {
|
||||||
return allSupportedSchemes.any((s) => s.name == scheme && s.requiresAuthority);
|
return allSupportedSchemes.any(
|
||||||
|
(s) => s.name == scheme && s.requiresAuthority,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
enum SchemePolicy {
|
enum SchemePolicy {
|
||||||
|
|||||||
Reference in New Issue
Block a user