Skip to content

Commit

Permalink
[auth] Add Support for Custom Icons (#4395)
Browse files Browse the repository at this point in the history
## Description
This PR introduces a new feature allowing users to select and assign
custom icons to their codes.
  • Loading branch information
ua741 authored Dec 17, 2024
2 parents 1978226 + 8fc9ff0 commit 9491310
Show file tree
Hide file tree
Showing 7 changed files with 433 additions and 7 deletions.
15 changes: 15 additions & 0 deletions auth/lib/models/all_icon_data.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
enum IconType { simpleIcon, customIcon }

class AllIconData {
final String title;
final IconType type;
final String? color;
final String? slug;

AllIconData({
required this.title,
required this.type,
required this.color,
this.slug,
});
}
16 changes: 16 additions & 0 deletions auth/lib/models/code_display.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ class CodeDisplay {
String note;
final List<String> tags;
int position;
String iconSrc;
String iconID;

CodeDisplay({
this.pinned = false,
Expand All @@ -21,8 +23,12 @@ class CodeDisplay {
this.tags = const [],
this.note = '',
this.position = 0,
this.iconSrc = '',
this.iconID = '',
});

bool get isCustomIcon => (iconSrc != '' && iconID != '');

// copyWith
CodeDisplay copyWith({
bool? pinned,
Expand All @@ -32,6 +38,8 @@ class CodeDisplay {
List<String>? tags,
String? note,
int? position,
String? iconSrc,
String? iconID,
}) {
final bool updatedPinned = pinned ?? this.pinned;
final bool updatedTrashed = trashed ?? this.trashed;
Expand All @@ -40,6 +48,8 @@ class CodeDisplay {
final List<String> updatedTags = tags ?? this.tags;
final String updatedNote = note ?? this.note;
final int updatedPosition = position ?? this.position;
final String updatedIconSrc = iconSrc ?? this.iconSrc;
final String updatedIconID = iconID ?? this.iconID;

return CodeDisplay(
pinned: updatedPinned,
Expand All @@ -49,6 +59,8 @@ class CodeDisplay {
tags: updatedTags,
note: updatedNote,
position: updatedPosition,
iconSrc: updatedIconSrc,
iconID: updatedIconID,
);
}

Expand All @@ -64,6 +76,8 @@ class CodeDisplay {
tags: List<String>.from(json['tags'] ?? []),
note: json['note'] ?? '',
position: json['position'] ?? 0,
iconSrc: json['iconSrc'] ?? 'ente',
iconID: json['iconID'] ?? '',
);
}

Expand Down Expand Up @@ -106,6 +120,8 @@ class CodeDisplay {
'tags': tags,
'note': note,
'position': position,
'iconSrc': iconSrc,
'iconID': iconID,
};
}

Expand Down
67 changes: 64 additions & 3 deletions auth/lib/onboarding/view/setup_enter_secret_key_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'dart:async';
import 'package:ente_auth/core/event_bus.dart';
import 'package:ente_auth/events/codes_updated_event.dart';
import "package:ente_auth/l10n/l10n.dart";
import 'package:ente_auth/models/all_icon_data.dart';
import 'package:ente_auth/models/code.dart';
import 'package:ente_auth/models/code_display.dart';
import 'package:ente_auth/onboarding/model/tag_enums.dart';
Expand All @@ -13,7 +14,10 @@ import 'package:ente_auth/onboarding/view/common/tag_chip.dart';
import 'package:ente_auth/store/code_display_store.dart';
import 'package:ente_auth/theme/ente_theme.dart';
import 'package:ente_auth/ui/components/buttons/button_widget.dart';
import 'package:ente_auth/ui/components/custom_icon_widget.dart';
import 'package:ente_auth/ui/components/models/button_result.dart';
import 'package:ente_auth/ui/custom_icon_page.dart';
import 'package:ente_auth/ui/utils/icon_utils.dart';
import 'package:ente_auth/utils/dialog_util.dart';
import 'package:ente_auth/utils/toast_util.dart';
import 'package:ente_auth/utils/totp_util.dart';
Expand Down Expand Up @@ -42,6 +46,9 @@ class _SetupEnterSecretKeyPageState extends State<SetupEnterSecretKeyPage> {
late List<String> selectedTags = [...?widget.code?.display.tags];
List<String> allTags = [];
StreamSubscription<CodesUpdatedEvent>? _streamSubscription;
bool isCustomIcon = false;
String _customIconID = "";
late IconType _iconSrc;

@override
void initState() {
Expand Down Expand Up @@ -81,6 +88,19 @@ class _SetupEnterSecretKeyPageState extends State<SetupEnterSecretKeyPage> {
_limitTextLength(_accountController, _otherTextLimit);
_limitTextLength(_secretController, _otherTextLimit);
}

isCustomIcon = widget.code?.display.isCustomIcon ?? false;
if (isCustomIcon) {
_customIconID = widget.code?.display.iconID ?? "ente";
} else {
if (widget.code != null) {
_customIconID = widget.code!.issuer;
}
}
_iconSrc = widget.code?.display.iconSrc == "simpleIcon"
? IconType.simpleIcon
: IconType.customIcon;

super.initState();
}

Expand Down Expand Up @@ -280,9 +300,21 @@ class _SetupEnterSecretKeyPageState extends State<SetupEnterSecretKeyPage> {
),
],
),
const SizedBox(
height: 40,
),
const SizedBox(height: 32),
if (widget.code != null)
CustomIconWidget(iconData: _customIconID),
const SizedBox(height: 24),
if (widget.code != null)
GestureDetector(
onTap: () async {
await navigateToCustomIconPage();
},
child: Text(
"Change Icon",
style: getEnteTextTheme(context).small,
),
),
const SizedBox(height: 40),
SizedBox(
width: 400,
child: OutlinedButton(
Expand Down Expand Up @@ -324,6 +356,11 @@ class _SetupEnterSecretKeyPageState extends State<SetupEnterSecretKeyPage> {
widget.code?.display.copyWith(tags: selectedTags) ??
CodeDisplay(tags: selectedTags);
display.note = notes;

display.iconID = _customIconID.toLowerCase();
display.iconSrc =
_iconSrc == IconType.simpleIcon ? 'simpleIcon' : 'customIcon';

if (widget.code != null && widget.code!.secret != secret) {
ButtonResult? result = await showChoiceActionSheet(
context,
Expand Down Expand Up @@ -373,4 +410,28 @@ class _SetupEnterSecretKeyPageState extends State<SetupEnterSecretKeyPage> {
message ?? context.l10n.pleaseVerifyDetails,
);
}

Future<void> navigateToCustomIconPage() async {
final allIcons = IconUtils.instance.getAllIcons();
String currentIcon;
if (widget.code!.display.isCustomIcon) {
currentIcon = widget.code!.display.iconID;
} else {
currentIcon = widget.code!.issuer;
}
final AllIconData newCustomIcon = await Navigator.of(context).push(
MaterialPageRoute(
builder: (context) {
return CustomIconPage(
currentIcon: currentIcon,
allIcons: allIcons,
);
},
),
);
setState(() {
_customIconID = newCustomIcon.title;
_iconSrc = newCustomIcon.type;
});
}
}
8 changes: 7 additions & 1 deletion auth/lib/ui/code_widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -445,13 +445,19 @@ class _CodeWidgetState extends State<CodeWidget> {
}

Widget _getIcon() {
final String iconData;
if (widget.code.display.isCustomIcon) {
iconData = widget.code.display.iconID;
} else {
iconData = widget.code.issuer;
}
return Padding(
padding: _shouldShowLargeIcon
? EdgeInsets.only(left: widget.isCompactMode ? 12 : 16)
: const EdgeInsets.all(0),
child: IconUtils.instance.getIcon(
context,
safeDecode(widget.code.issuer).trim(),
safeDecode(iconData).trim(),
width: widget.isCompactMode
? (_shouldShowLargeIcon ? 32 : 24)
: (_shouldShowLargeIcon ? 42 : 24),
Expand Down
37 changes: 37 additions & 0 deletions auth/lib/ui/components/custom_icon_widget.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import 'package:ente_auth/theme/ente_theme.dart';
import 'package:ente_auth/ui/utils/icon_utils.dart';
import 'package:ente_auth/utils/totp_util.dart';
import 'package:flutter/material.dart';

class CustomIconWidget extends StatelessWidget {
final String iconData;

CustomIconWidget({
super.key,
required this.iconData,
});

@override
Widget build(BuildContext context) {
return Container(
width: 70,
height: 70,
decoration: BoxDecoration(
border: Border.all(
width: 1.5,
color: getEnteColorScheme(context).tagChipSelectedColor,
),
borderRadius: const BorderRadius.all(Radius.circular(12.0)),
),
padding: const EdgeInsets.all(8),
child: FittedBox(
fit: BoxFit.contain,
child: IconUtils.instance.getIcon(
context,
safeDecode(iconData).trim(),
width: 50,
),
),
);
}
}
Loading

0 comments on commit 9491310

Please sign in to comment.