Skip to content

Commit

Permalink
テーマをインストールできるように
Browse files Browse the repository at this point in the history
  • Loading branch information
poppingmoon committed Jan 28, 2024
1 parent 07e6895 commit 73057c9
Show file tree
Hide file tree
Showing 10 changed files with 731 additions and 341 deletions.
1 change: 1 addition & 0 deletions lib/l10n/app_ja.arb
Original file line number Diff line number Diff line change
Expand Up @@ -795,6 +795,7 @@
}
},
"selectLightOrDarkMode": "ライトモード・ダークモードのつかいわけ",
"manageThemes": "テーマの管理",
"reaction": "リアクション",
"emojiTapReaction": "ノート内の絵文字タップでリアクションする",
"emojiTapReactionDescription": "ノート内の絵文字をタップしてリアクションします。MFMや外部サーバーの絵文字の場合うまく機能しないことがあります。",
Expand Down
12 changes: 12 additions & 0 deletions lib/providers.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import 'package:file/local.dart';
import 'package:flutter/widgets.dart';
import 'package:miria/model/account.dart';
import 'package:miria/model/acct.dart';
import 'package:miria/model/color_theme.dart';
import 'package:miria/model/tab_setting.dart';
import 'package:miria/repository/account_repository.dart';
import 'package:miria/repository/account_settings_repository.dart';
Expand All @@ -14,6 +15,7 @@ import 'package:miria/repository/favorite_repository.dart';
import 'package:miria/repository/general_settings_repository.dart';
import 'package:miria/repository/hybrid_timeline_repository.dart';
import 'package:miria/repository/import_export_repository.dart';
import 'package:miria/repository/color_theme_repository.dart';
import 'package:miria/repository/main_stream_repository.dart';
import 'package:miria/repository/global_time_line_repository.dart';
import 'package:miria/repository/home_time_line_repository.dart';
Expand Down Expand Up @@ -299,3 +301,13 @@ final antennasNotifierProvider = AsyncNotifierProvider.autoDispose

final clipsNotifierProvider = AsyncNotifierProvider.autoDispose
.family<ClipsNotifier, List<Clip>, Misskey>(ClipsNotifier.new);

final colorThemeRepositoryProvider =
NotifierProvider<ColorThemeRepository, List<ColorTheme>>(
ColorThemeRepository.new,
);

final installedThemeCodeRepositoryProvider =
NotifierProvider<InstalledThemeCodeRepository, List<String>>(
InstalledThemeCodeRepository.new,
);
88 changes: 88 additions & 0 deletions lib/repository/color_theme_repository.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:json5/json5.dart';
import 'package:miria/model/color_theme.dart';
import 'package:miria/model/misskey_theme.dart';
import 'package:miria/providers.dart';
import 'package:miria/view/common/error_dialog_handler.dart';
import 'package:miria/view/themes/built_in_color_themes.dart';
import 'package:shared_preferences/shared_preferences.dart';

class ColorThemeRepository extends Notifier<List<ColorTheme>> {
@override
List<ColorTheme> build() {
final codes = ref.watch(installedThemeCodeRepositoryProvider);
return [
...builtInColorThemes,
...codes.map((code) {
try {
return ColorTheme.misskey(
MisskeyTheme.fromJson(json5Decode(code) as Map<String, dynamic>),
);
} catch (e) {
return null;
}
}).nonNulls,
];
}

Future<void> addTheme(String code) async {
final ColorTheme theme;
try {
theme = ColorTheme.misskey(
MisskeyTheme.fromJson(json5Decode(code) as Map<String, dynamic>),
);
} catch (e) {
throw SpecifiedException("テーマの形式が間違っています");
}
if (state.any((element) => element.id == theme.id)) {
throw SpecifiedException("このテーマは既にインストールされています");
}

await ref
.read(installedThemeCodeRepositoryProvider.notifier)
.addTheme(code);
}

Future<void> removeTheme(String id) async {
await ref
.read(installedThemeCodeRepositoryProvider.notifier)
.removeTheme(id);
}
}

class InstalledThemeCodeRepository extends Notifier<List<String>> {
final prefsKey = "themes";

@override
List<String> build() {
Future(load);
return [];
}

Future<void> load() async {
final prefs = await SharedPreferences.getInstance();
final themes = prefs.getStringList(prefsKey);
if (themes == null) {
return;
}
state = themes;
}

Future<void> addTheme(String code) async {
final prefs = await SharedPreferences.getInstance();
final codes = prefs.getStringList(prefsKey) ?? [];
codes.add(code);
await prefs.setStringList(prefsKey, codes);
state = codes;
}

Future<void> removeTheme(String id) async {
final prefs = await SharedPreferences.getInstance();
final codes = prefs.getStringList(prefsKey);
codes!.removeWhere(
(code) => (json5Decode(code) as Map<String, dynamic>)["id"] == id,
);
await prefs.setStringList(prefsKey, codes);
state = codes;
}
}
2 changes: 2 additions & 0 deletions lib/router/app_router.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import 'package:miria/view/photo_edit_page/photo_edit_page.dart';
import 'package:miria/view/settings_page/account_settings_page/account_list.dart';
import 'package:miria/view/settings_page/app_info_page/app_info_page.dart';
import 'package:miria/view/settings_page/general_settings_page/general_settings_page.dart';
import 'package:miria/view/settings_page/general_settings_page/installed_themes_page.dart';
import 'package:miria/view/settings_page/import_export_page/import_export_page.dart';
import 'package:miria/view/settings_page/tab_settings_page/tab_settings_list_page.dart';
import 'package:miria/view/several_account_settings_page/cache_management_page/cache_management_page.dart';
Expand Down Expand Up @@ -102,5 +103,6 @@ class AppRouter extends _$AppRouter {
AutoRoute(page: MisskeyGamesRoute.page),
// きしょ……
AutoRoute(page: MisskeyRouteRoute.page),
AutoRoute(page: InstalledThemesRoute.page),
];
}
116 changes: 68 additions & 48 deletions lib/router/app_router.gr.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import 'package:google_fonts/google_fonts.dart';
import 'package:miria/const.dart';
import 'package:miria/model/general_settings.dart';
import 'package:miria/providers.dart';
import 'package:miria/router/app_router.dart';
import 'package:miria/view/themes/app_theme.dart';
import 'package:miria/view/themes/built_in_color_themes.dart';

@RoutePage()
class GeneralSettingsPage extends ConsumerStatefulWidget {
Expand Down Expand Up @@ -48,20 +48,23 @@ class GeneralSettingsPageState extends ConsumerState<GeneralSettingsPage> {
void didChangeDependencies() {
super.didChangeDependencies();
final settings = ref.read(generalSettingsRepositoryProvider).settings;
final colorThemes = ref.read(colorThemeRepositoryProvider);
setState(() {
lightModeTheme = settings.lightColorThemeId;
if (lightModeTheme.isEmpty) {
lightModeTheme = builtInColorThemes
.where((element) => !element.isDarkTheme)
.first
.id;
if (lightModeTheme.isEmpty ||
colorThemes.every(
(element) => element.isDarkTheme || element.id != lightModeTheme,
)) {
lightModeTheme =
colorThemes.firstWhere((element) => !element.isDarkTheme).id;
}
darkModeTheme = settings.darkColorThemeId;
if (darkModeTheme.isEmpty ||
builtInColorThemes.every((element) =>
!element.isDarkTheme || element.id != darkModeTheme)) {
colorThemes.every(
(element) => !element.isDarkTheme || element.id != darkModeTheme,
)) {
darkModeTheme =
builtInColorThemes.where((element) => element.isDarkTheme).first.id;
colorThemes.firstWhere((element) => element.isDarkTheme).id;
}
colorSystem = settings.themeColorSystem;
nsfwInherit = settings.nsfwInherit;
Expand Down Expand Up @@ -108,6 +111,7 @@ class GeneralSettingsPageState extends ConsumerState<GeneralSettingsPage> {

@override
Widget build(BuildContext context) {
final colorThemes = ref.watch(colorThemeRepositoryProvider);
return Scaffold(
appBar: AppBar(title: Text(S.of(context).generalSettings)),
body: SingleChildScrollView(
Expand Down Expand Up @@ -257,8 +261,9 @@ class GeneralSettingsPageState extends ConsumerState<GeneralSettingsPage> {
const Padding(padding: EdgeInsets.only(top: 10)),
Text(S.of(context).themeForLightMode),
DropdownButton<String>(
isExpanded: true,
items: [
for (final element in builtInColorThemes
for (final element in colorThemes
.where((element) => !element.isDarkTheme))
DropdownMenuItem(
value: element.id,
Expand All @@ -276,8 +281,9 @@ class GeneralSettingsPageState extends ConsumerState<GeneralSettingsPage> {
const Padding(padding: EdgeInsets.only(top: 10)),
Text(S.of(context).themeForDarkMode),
DropdownButton<String>(
isExpanded: true,
items: [
for (final element in builtInColorThemes
for (final element in colorThemes
.where((element) => element.isDarkTheme))
DropdownMenuItem(
value: element.id,
Expand All @@ -293,6 +299,7 @@ class GeneralSettingsPageState extends ConsumerState<GeneralSettingsPage> {
const Padding(padding: EdgeInsets.only(top: 10)),
Text(S.of(context).selectLightOrDarkMode),
DropdownButton<ThemeColorSystem>(
isExpanded: true,
items: [
for (final colorSystem in ThemeColorSystem.values)
DropdownMenuItem(
Expand All @@ -306,6 +313,13 @@ class GeneralSettingsPageState extends ConsumerState<GeneralSettingsPage> {
save();
}),
),
ListTile(
title: Text(S.of(context).manageThemes),
trailing: const Icon(Icons.keyboard_arrow_right),
onTap: () {
context.pushRoute(const InstalledThemesRoute());
},
),
],
),
),
Expand Down
Loading

0 comments on commit 73057c9

Please sign in to comment.