diff --git a/example/lib/nstack.dart b/example/lib/nstack.dart index e973bee..37164eb 100644 --- a/example/lib/nstack.dart +++ b/example/lib/nstack.dart @@ -1,62 +1,67 @@ -// Generated by NStack, do not modify this file. +/// Generated by NStack, do not modify this file. + import 'package:flutter/widgets.dart'; import 'package:nstack/models/language.dart'; import 'package:nstack/models/nstack_config.dart'; -import 'package:nstack/models/section_key.dart'; import 'package:nstack/nstack.dart'; import 'package:nstack/partial/section_key_delegate.dart'; // Update this file by running: -// - `flutter pub pub run build_runner build`, if your package depends on Flutter +// - `flutter pub run build_runner build`, if your package depends on Flutter // - `pub run build_runner build` otherwise - + class Localization { - _DefaultSection _defaultSection = _DefaultSection(); - _DefaultSection get defaultSection => _defaultSection; - _Error _error = _Error(); - _Error get error => _error; - _Test _test = _Test(); - _Test get test => _test; + final defaultSection = const _DefaultSection(); + final error = const _Error(); + final test = const _Test(); + + const Localization(); } class _DefaultSection extends SectionKeyDelegate { - String get hi => get(SectionKey("default", "hi", "Hej")); - String get cancel => get(SectionKey("default", "cancel", "Annuller")); - String get no => get(SectionKey("default", "no", "nej")); - String get yes => get(SectionKey("default", "yes", "Ja")); - String get edit => get(SectionKey("default", "edit", "Redigere")); - String get next => get(SectionKey("default", "next", "Næste")); - String get on => get(SectionKey("default", "on", "Tændt")); - String get off => get(SectionKey("default", "off", "af")); - String get ok => get(SectionKey("default", "ok", "Ok")); + const _DefaultSection(): super('default'); + + String get hi => get('hi', 'Hej'); + String get cancel => get('cancel', 'Annuller'); + String get no => get('no', 'nej'); + String get yes => get('yes', 'Ja'); + String get edit => get('edit', 'Redigere'); + String get next => get('next', 'Næste'); + String get on => get('on', 'Tændt'); + String get off => get('off', 'af'); + String get ok => get('ok', 'Ok'); } class _Error extends SectionKeyDelegate { - String get errorRandom => get(SectionKey("error", "errorRandom", "Helt tilfældig fejl")); - String get errorTitle => get(SectionKey("error", "errorTitle", "Fejl")); - String get authenticationError => get(SectionKey("error", "authenticationError", "Login er udløbet, login venligst ind igen.")); - String get connectionError => get(SectionKey("error", "connectionError", "Ingen eller dårlig forbindelse, prøv igen!")); - String get unknownError => get(SectionKey("error", "unknownError", "Ukendt fejl, prøv igen.")); + const _Error(): super('error'); + + String get errorRandom => get('errorRandom', 'Helt tilfældig fejl'); + String get errorTitle => get('errorTitle', 'Fejl'); + String get authenticationError => get('authenticationError', 'Login er udløbet, login venligst ind igen.'); + String get connectionError => get('connectionError', 'Ingen eller dårlig forbindelse, prøv igen!'); + String get unknownError => get('unknownError', 'Ukendt fejl, prøv igen.'); } class _Test extends SectionKeyDelegate { - String get title => get(SectionKey("test", "title", "NStack Demo")); - String get message => get(SectionKey("test", "message", "Bacon ipsum dolor amet magna meatball jerky in, shank sunt do burgdoggen spare ribs. Lorem boudin eiusmod short ribs pastrami. Sausage bresaola do turkey, dolor qui tail ground round culpa boudin nulla minim sunt beef ribs ham. Cillum in pastrami adipisicing swine lorem, velit sunt meatloaf bresaola short loin fugiat tri-tip boudin.")); - String get subTitle => get(SectionKey("test", "subTitle", "Subtitle demo")); - String get on => get(SectionKey("test", "on", "on")); - String get off => get(SectionKey("test", "off", "off")); + const _Test(): super('test'); + + String get title => get('title', 'NStack Demo'); + String get message => get('message', 'Bacon ipsum dolor amet magna meatball jerky in, shank sunt do burgdoggen spare ribs. Lorem boudin eiusmod short ribs pastrami. Sausage bresaola do turkey, dolor qui tail ground round culpa boudin nulla minim sunt beef ribs ham. Cillum in pastrami adipisicing swine lorem, velit sunt meatloaf bresaola short loin fugiat tri-tip boudin.'); + String get subTitle => get('subTitle', 'Subtitle demo'); + String get on => get('on', 'on'); + String get off => get('off', 'off'); } -final _config = NStackConfig(projectId: "bOdrNuZd4syxuAz6gyCb3xwBCjA8U4h4IcQI", apiKey: "X0ENl5QpKI51tS9CzKSt1PGwfZeq2gBMTU58"); +const _config = NStackConfig(projectId: 'bOdrNuZd4syxuAz6gyCb3xwBCjA8U4h4IcQI', apiKey: 'X0ENl5QpKI51tS9CzKSt1PGwfZeq2gBMTU58'); -final _languages = [ - Language(id: 6, locale: "da-DK", direction: "LRM", isDefault: true, isBestFit: false), - Language(id: 11, locale: "en-GB", direction: "LRM", isDefault: false, isBestFit: true), - Language(id: 15, locale: "es-MX", direction: "LRM", isDefault: false, isBestFit: false), - Language(id: 20, locale: "fr-FR", direction: "LRM", isDefault: false, isBestFit: false), +const _languages = [ + Language(id: 6, locale: 'da-DK', direction: 'LRM', isDefault: true, isBestFit: false), + Language(id: 11, locale: 'en-GB', direction: 'LRM', isDefault: false, isBestFit: true), + Language(id: 15, locale: 'es-MX', direction: 'LRM', isDefault: false, isBestFit: false), + Language(id: 20, locale: 'fr-FR', direction: 'LRM', isDefault: false, isBestFit: false), ]; -final _bundledTranslations = { +const _bundledTranslations = { 'da-DK': '{"data":{"default":{"hi":"Hej","cancel":"Annuller","no":"nej","yes":"Ja","edit":"Redigere","next":"N\u00e6ste","on":"T\u00e6ndt","off":"af","ok":"Ok"},"error":{"errorRandom":"Helt tilf\u00e6ldig fejl","errorTitle":"Fejl","authenticationError":"Login er udl\u00f8bet, login venligst ind igen.","connectionError":"Ingen eller d\u00e5rlig forbindelse, pr\u00f8v igen!","unknownError":"Ukendt fejl, pr\u00f8v igen."},"test":{"title":"NStack Demo","message":"Bacon ipsum dolor amet magna meatball jerky in, shank sunt do burgdoggen spare ribs. Lorem boudin eiusmod short ribs pastrami. Sausage bresaola do turkey, dolor qui tail ground round culpa boudin nulla minim sunt beef ribs ham. Cillum in pastrami adipisicing swine lorem, velit sunt meatloaf bresaola short loin fugiat tri-tip boudin.","subTitle":"Subtitle demo","on":"on","off":"off"}},"meta":{"language":{"id":6,"name":"Danish","locale":"da-DK","direction":"LRM","is_default":false,"is_best_fit":false},"platform":{"id":30,"slug":"mobile"}}}', 'en-GB': '{"data":{"default":{"hi":"Hi","cancel":"Cancel","no":"No","yes":"Yes","edit":"Edit","next":"Next","on":"On","off":"Off","ok":"Ok"},"error":{"errorRandom":"Totally random error","errorTitle":"Error","authenticationError":"Login expired, please login again.","connectionError":"No or bad connection, please try again.","unknownError":"Unknown error, please try again."},"test":{"title":"NStack Demo","message":"Bacon ipsum dolor amet magna meatball jerky in, shank sunt do burgdoggen spare ribs. Lorem boudin eiusmod short ribs pastrami. Sausage bresaola do turkey, dolor qui tail ground round culpa boudin nulla minim sunt beef ribs ham. Cillum in pastrami adipisicing swine lorem, velit sunt meatloaf bresaola short loin fugiat tri-tip boudin.","subTitle":"Subtitle demo","on":"on","off":"off"}},"meta":{"language":{"id":11,"name":"English (UK)","locale":"en-GB","direction":"LRM","is_default":false,"is_best_fit":false},"platform":{"id":30,"slug":"mobile"}}}', 'es-MX': '{"data":{"default":{"hi":"Hola","cancel":"Cancelar","no":"no","yes":"Si","edit":"Editar","next":"Siguiente","on":"Apprendido","off":"Apagado","ok":"__ok"},"error":{"errorRandom":"__errorRandom","errorTitle":"__errorTitle","authenticationError":"__authenticationError","connectionError":"__connectionError","unknownError":"__unknownError"},"test":{"title":"__title","message":"__message","subTitle":"__subTitle","on":"__on","off":"__off"}},"meta":{"language":{"id":15,"name":"Spanish (Mexico)","locale":"es-MX","direction":"LRM","is_default":false,"is_best_fit":false},"platform":{"id":30,"slug":"mobile"}}}', @@ -65,9 +70,9 @@ final _bundledTranslations = { final _nstack = NStack( config: _config, - localization: Localization(), + localization: const Localization(), availableLanguages: _languages, - bundledTranslations: _bundledTranslations + bundledTranslations: _bundledTranslations, ); class NStackWidget extends InheritedWidget { @@ -85,10 +90,6 @@ class NStackWidget extends InheritedWidget { nstack != oldWidget.nstack; } -extension NStackWidgetExtension on BuildContext { - Localization get localization => NStackWidget.of(this).localization; -} - class NStackInitWidget extends StatefulWidget { final Widget child; @@ -119,3 +120,13 @@ class _NStackInitState extends State { } } +/// Allows to access the Nstack Localization using the BuildContext +extension NStackWidgetExtension on BuildContext { + Localization get localization => NStackWidget.of(this).localization; +} + +/// Allows to access the Nstack Localization from StatefulWidget's State +extension NStackStateExtension on State { + Localization get localization => context.localization; +} + diff --git a/example/lib/nstack.json b/example/lib/nstack.json index 68ba23b..31c83f7 100644 --- a/example/lib/nstack.json +++ b/example/lib/nstack.json @@ -1,4 +1,5 @@ { + "version": 1, "nstack_project_id": "bOdrNuZd4syxuAz6gyCb3xwBCjA8U4h4IcQI", "nstack_api_key": "X0ENl5QpKI51tS9CzKSt1PGwfZeq2gBMTU58" } \ No newline at end of file diff --git a/lib/models/app_open.dart b/lib/models/app_open.dart new file mode 100644 index 0000000..8bab642 --- /dev/null +++ b/lib/models/app_open.dart @@ -0,0 +1,20 @@ +import 'package:meta/meta.dart'; +import 'package:nstack/models/app_open_data.dart'; +import 'package:nstack/models/app_open_meta.dart'; + +class AppOpen { + final AppOpenData data; + final AppOpenMeta meta; + + AppOpen({ + @required this.data, + @required this.meta, + }); + + factory AppOpen.fromJson(Map json) { + return AppOpen( + data: AppOpenData.fromJson(json['data']), + meta: AppOpenMeta.fromJson(json['meta']), + ); + } +} diff --git a/lib/models/app_open_data.dart b/lib/models/app_open_data.dart new file mode 100644 index 0000000..70fd45e --- /dev/null +++ b/lib/models/app_open_data.dart @@ -0,0 +1,53 @@ +import 'package:meta/meta.dart'; +import 'package:nstack/models/app_update.dart'; +import 'package:nstack/models/localize_index.dart'; +import 'package:nstack/models/message.dart'; +import 'package:nstack/models/rate_reminder.dart'; +import 'package:nstack/models/terms.dart'; +import 'package:nstack/other/extensions.dart'; + +class AppOpenData { + final int count; + final AppUpdate update; + final List localize; + final String platform; + final DateTime createdAt; + final DateTime updatedAt; + final Message message; + final RateReminder rateReminder; + final List terms; + + AppOpenData({ + @required this.count, + @required this.update, + @required this.localize, + @required this.platform, + @required this.createdAt, + @required this.updatedAt, + @required this.message, + @required this.rateReminder, + @required this.terms, + }); + + factory AppOpenData.fromJson(Map json) { + return AppOpenData( + count: json['count'], + update: AppUpdate.fromJson(json['update']), + localize: (json['localize'] as List)?.let( + (it) => it.map((e) => LocalizeIndex.fromJson(e)).toList(), + ), + platform: json['platform'], + createdAt: (json['created_at'] as String)?.let( + (it) => DateTime.parse(it), + ), + updatedAt: (json['last_updated'] as String)?.let( + (it) => DateTime.parse(it), + ), + message: (json['message'] as Map)?.let((it) => Message.fromJson(it)), + rateReminder: json['rateReminder']?.let( + (it) => RateReminder.fromJson(it), + ), + terms: json['terms']?.let((it) => it), + ); + } +} diff --git a/lib/models/app_open_meta.dart b/lib/models/app_open_meta.dart new file mode 100644 index 0000000..7095f72 --- /dev/null +++ b/lib/models/app_open_meta.dart @@ -0,0 +1,13 @@ +import 'package:meta/meta.dart'; + +class AppOpenMeta { + final String acceptLanguage; + + AppOpenMeta({@required this.acceptLanguage}); + + factory AppOpenMeta.fromJson(Map json) { + return AppOpenMeta( + acceptLanguage: json['accept_Language'], + ); + } +} diff --git a/lib/models/app_update.dart b/lib/models/app_update.dart new file mode 100644 index 0000000..5787f23 --- /dev/null +++ b/lib/models/app_update.dart @@ -0,0 +1,42 @@ +import 'package:meta/meta.dart'; +import 'package:nstack/models/app_update_state.dart'; +import 'package:nstack/models/update_info.dart'; +import 'package:nstack/other/extensions.dart'; + +class AppUpdate { + final UpdateInfo newerVersion; + final UpdateInfo newInVersion; + + AppUpdate({ + @required this.newerVersion, + @required this.newInVersion, + }); + + factory AppUpdate.fromJson(Map json) { + return AppUpdate( + newerVersion: (json['newer_version'] as Map)?.let( + (it) => UpdateInfo.fromJson(it), + ), + newInVersion: (json['new_in_version'] as Map)?.let( + (it) => UpdateInfo.fromJson(it), + ), + ); + } + + UpdateInfo get update { + return newerVersion ?? newInVersion; + } + + AppUpdateState get state { + if (update?.state == "yes") { + return AppUpdateState.update; + } + if (update?.state == "force") { + return AppUpdateState.force; + } + if (newInVersion != null) { + return AppUpdateState.changelog; + } + return AppUpdateState.none; + } +} diff --git a/lib/models/app_update_state.dart b/lib/models/app_update_state.dart new file mode 100644 index 0000000..b9323c6 --- /dev/null +++ b/lib/models/app_update_state.dart @@ -0,0 +1,6 @@ +enum AppUpdateState { + none, + update, + force, + changelog, +} diff --git a/lib/models/language.dart b/lib/models/language.dart index 126897c..edd75c9 100644 --- a/lib/models/language.dart +++ b/lib/models/language.dart @@ -1,19 +1,28 @@ class Language { - int id; - String name; - String locale; - String direction; - bool isDefault; - bool isBestFit; + final int id; + final String name; + final String locale; + final String direction; + final bool isDefault; + final bool isBestFit; - Language({this.id, this.name, this.locale, this.direction, this.isDefault, this.isBestFit}); + const Language({ + this.id, + this.name, + this.locale, + this.direction, + this.isDefault, + this.isBestFit, + }); - Language.fromJson(Map json) { - id = json['id']; - name = json['name']; - locale = json['locale']; - direction = json['direction']; - isDefault = json['is_default']; - isBestFit = json['is_best_fit']; - } -} \ No newline at end of file + factory Language.fromJson(Map json) { + return Language( + id: json['id'], + name: json['name'], + locale: json['locale'], + direction: json['direction'], + isDefault: json['is_default'], + isBestFit: json['is_best_fit'], + ); + } +} diff --git a/lib/models/language_response.dart b/lib/models/language_response.dart new file mode 100644 index 0000000..1925f6f --- /dev/null +++ b/lib/models/language_response.dart @@ -0,0 +1,9 @@ +class LocalizationData { + final Map data; + + LocalizationData(this.data); + + factory LocalizationData.fromJson(Map json) { + return LocalizationData(json['data']); + } +} diff --git a/lib/models/localize_index.dart b/lib/models/localize_index.dart new file mode 100644 index 0000000..ccf4cfb --- /dev/null +++ b/lib/models/localize_index.dart @@ -0,0 +1,34 @@ +import 'package:nstack/models/language.dart'; +import 'package:nstack/other/extensions.dart'; + +class LocalizeIndex { + final int id; + final String url; + final DateTime lastUpdatedAt; + final bool shouldUpdate; + final Language language; + + LocalizeIndex({ + this.id, + this.url, + this.lastUpdatedAt, + this.shouldUpdate, + this.language, + }); + + factory LocalizeIndex.fromJson(Map json) { + return LocalizeIndex( + id: json['id'], + url: json['url'], + lastUpdatedAt: + (json['last_updated_at'] as String)?.let((it) => DateTime.parse(it)), + shouldUpdate: json['should_update'] ?? false, + language: (json['language'] as Map)?.let((it) => Language.fromJson(it)), + ); + } + + @override + String toString() { + return 'LocalizeIndex(id: $id, url $url, lastUpdatedAt: $lastUpdatedAt, shouldUpdate: $shouldUpdate, language: $language)'; + } +} diff --git a/lib/models/message.dart b/lib/models/message.dart new file mode 100644 index 0000000..0fb81cb --- /dev/null +++ b/lib/models/message.dart @@ -0,0 +1,83 @@ +import 'package:meta/meta.dart'; +import 'package:nstack/other/extensions.dart'; + +class Message { + final int id; + final int applicationId; + final MessageShowSetting showSetting; + final int viewCount; + final String message; + final String url; + final DateTime createdAt; + final DateTime updatedAt; + final DateTime deletedAt; + final int projectId; + final MessageLocalization localization; + + Message({ + @required this.id, + @required this.applicationId, + @required this.showSetting, + @required this.viewCount, + @required this.message, + @required this.url, + @required this.createdAt, + @required this.updatedAt, + @required this.deletedAt, + @required this.localization, + @required this.projectId, + }); + + factory Message.fromJson(Map json) { + return Message( + id: json['id'], + applicationId: json['application_id'], + showSetting: (json['show_setting'] as String)?.toMessageShowSetting(), + message: json['message'], + viewCount: json['view_count'], + url: json['url'], + createdAt: + (json['created_at'] as String)?.let((it) => DateTime.parse(it)), + updatedAt: + (json['updated_at'] as String)?.let((it) => DateTime.parse(it)), + deletedAt: + (json['deleted_at'] as String)?.let((it) => DateTime.parse(it)), + projectId: json['project_id'], + localization: json['localization']?.let( + (it) => MessageLocalization.fromJson(it), + ), + ); + } +} + +enum MessageShowSetting { + once, + always, +} + +extension on String { + MessageShowSetting toMessageShowSetting() { + switch (this) { + case 'show_once': + return MessageShowSetting.once; + case 'show_always': + return MessageShowSetting.always; + default: + return null; + } + } +} + +class MessageLocalization { + final String okBtn; + final String urlBtn; + + MessageLocalization({this.okBtn, this.urlBtn}); + + factory MessageLocalization.fromJson(Map json) { + return MessageLocalization( + okBtn: json['okBtn'], + urlBtn: json['urlBtn'], + ); + } +} diff --git a/lib/models/nstack_appopen_data.dart b/lib/models/nstack_appopen_data.dart index fbed852..4e39847 100644 --- a/lib/models/nstack_appopen_data.dart +++ b/lib/models/nstack_appopen_data.dart @@ -1,14 +1,16 @@ +import 'package:meta/meta.dart'; + class NStackAppOpenData { - final String platform = "android"; - final String guid; - final String version; - final String oldVersion; - final String lastUpdated; + final String platform = "android"; + final String guid; + final String version; + final String oldVersion; + final String lastUpdated; - NStackAppOpenData({ - this.guid, - this.version, - this.oldVersion, - this.lastUpdated - }); -} \ No newline at end of file + const NStackAppOpenData({ + @required this.guid, + @required this.version, + @required this.oldVersion, + @required this.lastUpdated, + }); +} diff --git a/lib/models/nstack_config.dart b/lib/models/nstack_config.dart index e27f557..029fdaa 100644 --- a/lib/models/nstack_config.dart +++ b/lib/models/nstack_config.dart @@ -1,6 +1,11 @@ +import 'package:meta/meta.dart'; + class NStackConfig { final String projectId; final String apiKey; - NStackConfig({this.projectId, this.apiKey}); + const NStackConfig({ + @required this.projectId, + @required this.apiKey, + }); } diff --git a/lib/models/rate_reminder.dart b/lib/models/rate_reminder.dart new file mode 100644 index 0000000..ade9a89 --- /dev/null +++ b/lib/models/rate_reminder.dart @@ -0,0 +1,28 @@ +class RateReminder { + final String title; + final String body; + final String yesButton; + final String laterButton; + final String noButton; + final String link; + + RateReminder({ + this.title, + this.body, + this.yesButton, + this.laterButton, + this.noButton, + this.link, + }); + + factory RateReminder.fromJson(Map json) { + return RateReminder( + title: json['title'], + body: json['body'], + yesButton: json['yesButton'], + laterButton: json['laterButton'], + noButton: json['noButton'], + link: json['link'], + ); + } +} diff --git a/lib/models/section_key.dart b/lib/models/section_key.dart deleted file mode 100644 index 83af0b1..0000000 --- a/lib/models/section_key.dart +++ /dev/null @@ -1,7 +0,0 @@ -class SectionKey { - final String sectionName; - final String keyName; - final String fallbackValue; - - SectionKey(this.sectionName, this.keyName, this.fallbackValue); -} \ No newline at end of file diff --git a/lib/models/terms.dart b/lib/models/terms.dart new file mode 100644 index 0000000..0bb667a --- /dev/null +++ b/lib/models/terms.dart @@ -0,0 +1,40 @@ +import 'package:meta/meta.dart'; + +class Terms { + final int id; + final TermsType type; + final String name; + final String slug; + final TermsVersion version; + + Terms({ + @required this.id, + @required this.type, + @required this.name, + @required this.slug, + @required this.version, + }); +} + +class TermsType { + final int id; + final String version; + final String name; + final DateTime publishedAt; + final bool hasViewed; + + TermsType({ + this.id, + this.version, + this.name, + this.publishedAt, + this.hasViewed, + }); +} + +enum TermsVersion { + termsConditions, + privacy, + cookie, + other, +} diff --git a/lib/models/update_info.dart b/lib/models/update_info.dart new file mode 100644 index 0000000..cbeb81d --- /dev/null +++ b/lib/models/update_info.dart @@ -0,0 +1,32 @@ +import 'package:meta/meta.dart'; +import 'package:nstack/models/update_translate.dart'; +import 'package:nstack/other/extensions.dart'; + +class UpdateInfo { + final String state; + final int lastId; + final String version; + final String link; + final UpdateTranslate translate; + final String fileUrl; + + UpdateInfo({ + @required this.state, + @required this.lastId, + @required this.version, + @required this.link, + @required this.translate, + @required this.fileUrl, + }); + + factory UpdateInfo.fromJson(Map json) { + return UpdateInfo( + state: json['state'], + lastId: json['last_id'], + version: json['version'], + link: json['link'], + translate: (json['translate'] as Map)?.let((it) => UpdateTranslate.fromJson(it)), + fileUrl: json['file_url'], + ); + } +} diff --git a/lib/models/update_translate.dart b/lib/models/update_translate.dart new file mode 100644 index 0000000..4b5f34c --- /dev/null +++ b/lib/models/update_translate.dart @@ -0,0 +1,24 @@ +import 'package:meta/meta.dart'; + +class UpdateTranslate { + final String title; + final String message; + final String positiveButton; + final String negativeButton; + + UpdateTranslate({ + @required this.title, + @required this.message, + @required this.positiveButton, + @required this.negativeButton, + }); + + factory UpdateTranslate.fromJson(Map json) { + return UpdateTranslate( + title: json['title'], + message: json['message'], + positiveButton: json['positiveBtn'], + negativeButton: json['negativeBtn'], + ); + } +} diff --git a/lib/nstack.dart b/lib/nstack.dart index d937787..fc56d12 100644 --- a/lib/nstack.dart +++ b/lib/nstack.dart @@ -1,7 +1,10 @@ import 'package:cross_local_storage/cross_local_storage.dart'; -import 'package:flutter/cupertino.dart'; import 'package:flutter/services.dart'; +import 'package:flutter/widgets.dart'; import 'package:get_version/get_version.dart'; +import 'package:nstack/models/app_open.dart'; +import 'package:nstack/models/language.dart'; +import 'package:nstack/models/language_response.dart'; import 'package:nstack/models/nstack_appopen_data.dart'; import 'package:nstack/models/nstack_config.dart'; import 'package:nstack/src/repository.dart'; @@ -12,27 +15,31 @@ import 'dart:convert'; class NStack { final NStackConfig config; final BuildContext context; - T localization; + final T localization; final String prefsKeyLastUpdated = "nstack_last_updated"; final String prefsKeyGuid = "nstack_guid"; - final NStackRepository _repository = NStackRepository(); + final NStackRepository _repository; - NStackAppOpenData appOpenData; + NStackAppOpenData _appOpenData; NStack({ - this.config, - this.context, - this.localization, - availableLanguages, - bundledTranslations, - pickedLanguageLocale - }) { - Repository().setupLocalization(bundledTranslations, availableLanguages, pickedLanguageLocale); + @required this.config, + @required this.context, + @required this.localization, + @required List availableLanguages, + @required Map bundledTranslations, + @required String pickedLanguageLocale, + }) : _repository = NStackRepository(config) { + LocalizationRepository().setupLocalization( + bundledTranslations, + availableLanguages, + pickedLanguageLocale, + ); } - Future _setupAppOpenData() async { + Future _setupAppOpenData() async { WidgetsFlutterBinding.ensureInitialized(); LocalStorageInterface prefs = await LocalStorage.getInstance(); String projectVersion; @@ -45,26 +52,25 @@ class NStack { projectVersion = '1'; } - - if(prefs.containsKey(prefsKeyGuid)) { + if (prefs.containsKey(prefsKeyGuid)) { guid = prefs.getString(prefsKeyGuid); } else { guid = Uuid().v1(); prefs.setString(prefsKeyGuid, guid); } - if(prefs.containsKey(prefsKeyLastUpdated)) { + if (prefs.containsKey(prefsKeyLastUpdated)) { lastUpdated = prefs.getString(prefsKeyLastUpdated); } else { lastUpdated = DateTime.utc(1980, 1, 1).toIso8601String(); prefs.setString(prefsKeyLastUpdated, lastUpdated); } - appOpenData = NStackAppOpenData( + _appOpenData = NStackAppOpenData( guid: guid, lastUpdated: lastUpdated, oldVersion: projectVersion, - version: projectVersion + version: projectVersion, ); } @@ -72,60 +78,67 @@ class NStack { try { await _setupAppOpenData(); - _repository.updateHeaders(config.projectId, config.apiKey); - print("NStack --> Calling App Open..."); - var result = await _repository.postAppOpen( - acceptHeader: locale.toLanguageTag(), - appOpenData: appOpenData, - devMode: false, - testMode: false + final Map result = await _repository.postAppOpen( + acceptHeader: locale.toLanguageTag(), + appOpenData: _appOpenData, + devMode: false, + testMode: false, ); - LocalStorageInterface prefs = await LocalStorage.getInstance(); + final appOpen = AppOpen.fromJson(result); + + final LocalStorageInterface prefs = await LocalStorage.getInstance(); // Find best fit - var bestFitLanguage = result['data']['localize'].where((element) => element['language']['is_best_fit'] == true).first; - var nstackKey = "nstack_lang_" + bestFitLanguage['language']['locale']; - - // Update or use cache? - if(bestFitLanguage['should_update'] == true) { - // Updating... - print("NStack --> Fetching best fit language: " + bestFitLanguage['language']['locale']); - var bestFitLanguageResponse = await _repository.fetchLocalizationForLanguage(bestFitLanguage); + final bestFitLanguage = appOpen.data.localize + .where((localize) => localize.language.isBestFit == true) + .first; + + final nstackKey = 'nstack_lang_${bestFitLanguage.language.locale}'; + + // Fetch from the server or use the cache? + if (bestFitLanguage.shouldUpdate == true) { + // Fetch best fit language from the server + print( + 'NStack --> Fetching best fit language: ${bestFitLanguage.language.locale}'); + final String bestFitLanguageResponse = + await _repository.fetchLocalizationForLanguage(bestFitLanguage); //localization = localization.fromJson(json.decode(bestFitLanguageResponse)["data"]); - Repository().updateLocalization(bestFitLanguageResponse['data'], bestFitLanguage['language']['locale']); + final translationJson = + LocalizationData.fromJson(jsonDecode(bestFitLanguageResponse)); + LocalizationRepository().updateLocalization( + translationJson.data, bestFitLanguage.language.locale); // Update cache for key prefs.setString(nstackKey, bestFitLanguageResponse); // Update last_updated for next app open call prefs.setString(prefsKeyLastUpdated, DateTime.now().toIso8601String()); } else { - - // Using cache - if(prefs.containsKey(nstackKey)) { - print("NStack --> Using cache for best fit language: " + bestFitLanguage['language']['locale']); - var cachedResponse = json.decode(prefs.getString(nstackKey)); + // Using best fit language from the cache + if (prefs.containsKey(nstackKey)) { + print( + 'NStack --> Using cache for best fit language: ${bestFitLanguage.language.locale}'); + final cachedResponse = json.decode(prefs.getString(nstackKey)); + final languageResponse = LocalizationData.fromJson(cachedResponse); //localization = localization.fromJson(cachedResponse['data']); - Repository().updateLocalization(cachedResponse['data'], bestFitLanguage['language']['locale']); - // No cache, default values (this shouldn't happen, should_update should be true) + LocalizationRepository().updateLocalization( + languageResponse.data, bestFitLanguage.language.locale); + // No cache, default values (this shouldn't happen, should_update should be true) } else { - print("NStack --> WARNING: No cache found for best fit language: " + bestFitLanguage['language']['locale']); + print( + 'NStack --> WARNING: No cache found for best fit language: ${bestFitLanguage.language.locale}'); } } - print("NStack --> Updated localization..."); - + print('NStack --> Updated localization.'); return AppOpenResult.success; } catch (e, s) { - print("NStack --> App Open failed because of: " + e.toString()); + print('NStack --> App Open failed because of: ${e.toString()}'); print(s); return AppOpenResult.failed; } } } -enum AppOpenResult { - success, - failed -} +enum AppOpenResult { success, failed } diff --git a/lib/other/extensions.dart b/lib/other/extensions.dart new file mode 100644 index 0000000..05daf62 --- /dev/null +++ b/lib/other/extensions.dart @@ -0,0 +1,48 @@ +extension DartEx on T { + R let(R Function(T it) block) { + return block(this); + } + + T takeIf(bool Function(T it) condition) { + return condition(this) ? this : null; + } +} + +extension MapEx on Map { + T keyFromValue(K value) => entries + .firstWhere((entry) => entry.value == value, orElse: () => null) + ?.key; + + K getOrNull(T key) { + if (this == null || !this.containsKey(key)) { + return null; + } else { + return this[key]; + } + } + + K getOrElse(T key, K fallback) { + return this.getOrNull(key) ?? fallback; + } +} + +void throwIf(bool test, Error Function() errorFactoryFunc) { + return throwIfNot(!test, errorFactoryFunc); +} + +void throwIfNot(bool test, Error Function() errorFactoryFunc) { + if (!test) { + throw errorFactoryFunc(); + } +} + +extension StringEx on String { + bool get isNullOrEmpty => this == null || this.isEmpty; + + bool get isNullOrBlank => this == null || this.isEmpty || this.trim().isEmpty; + + bool get isNotNullOrEmpty => this != null && this.isNotEmpty; + + bool get isNotNullOrBlank => + this != null && this.isNotEmpty && this.trim().isNotEmpty; +} diff --git a/lib/other/reserved_kwywords.dart b/lib/other/reserved_kwywords.dart new file mode 100644 index 0000000..0bee7fa --- /dev/null +++ b/lib/other/reserved_kwywords.dart @@ -0,0 +1,90 @@ +/// Utility class to check for reserved Dart keywords. +class DartKeywords { + static const _list = [ + 'abstract', + 'all', + 'as', + 'assert', + 'async', + 'await', + 'boolean', + 'break', + 'byte', + 'case', + 'catch', + 'char', + 'class', + 'const', + 'continue', + 'covariant', + 'default', + 'deferred', + 'description', + 'do', + 'double', + 'dynamic', + 'else', + 'enum', + 'export', + 'extends', + 'extension', + 'external', + 'factory', + 'false', + 'final', + 'finally', + 'float', + 'for', + 'Function', + 'get', + 'goto', + 'hide', + 'if', + 'implements', + 'import', + 'in', + 'instanceof', + 'int', + 'interface', + 'is', + 'library', + 'long', + 'mixin', + 'native', + 'new', + 'null', + 'on', + 'operator', + 'package', + 'part', + 'private', + 'protected', + 'public', + 'rethrow', + 'return', + 'set', + 'short', + 'show', + 'static', + 'strictfp', + 'struct', + 'super', + 'switch', + 'sync', + 'synchronized', + 'this', + 'throw', + 'throws', + 'transient', + 'try', + 'typedef', + 'var', + 'void', + 'volatile', + 'while', + 'with', + 'yield', + ]; + + static bool isReserved(String keyword) => _list.contains(keyword); +} diff --git a/lib/partial/section_key_delegate.dart b/lib/partial/section_key_delegate.dart index 9ccd25e..bd51093 100644 --- a/lib/partial/section_key_delegate.dart +++ b/lib/partial/section_key_delegate.dart @@ -1,7 +1,9 @@ +import 'package:nstack/src/repository.dart'; -import '../models/section_key.dart'; -import '../src/repository.dart'; +abstract class SectionKeyDelegate { + final String sectionKey; -class SectionKeyDelegate { - String get(SectionKey sectionKey) => Repository().getSectionKeyValue(sectionKey); + const SectionKeyDelegate(this.sectionKey); + + String get(String key, String fallbackValue) => LocalizationRepository().getSectionKeyValue(sectionKey, key, fallbackValue); } \ No newline at end of file diff --git a/lib/src/nstack_builder.dart b/lib/src/nstack_builder.dart index a842316..2d69fd3 100644 --- a/lib/src/nstack_builder.dart +++ b/lib/src/nstack_builder.dart @@ -2,6 +2,12 @@ import 'dart:async'; import 'dart:convert'; import 'package:build/build.dart'; +import 'package:nstack/models/language.dart'; +import 'package:nstack/models/language_response.dart'; +import 'package:nstack/models/localize_index.dart'; +import 'package:nstack/models/nstack_config.dart'; +import 'package:nstack/other/extensions.dart'; +import 'package:nstack/other/reserved_kwywords.dart'; import 'package:nstack/src/nstack_repository.dart'; /// A builder which outputs a 'nstack.dart' file. This file provides a NStack instance, type safe section key accessors and all bundled translations. @@ -10,62 +16,61 @@ class NstackBuilder implements Builder { @override FutureOr build(BuildStep buildStep) async { - NStackRepository repository = NStackRepository(); /// Read the input source and parse it as JSON. - Map input; - String projectId; - String apiKey; final AssetId outputId = buildStep.inputId.changeExtension('.dart'); - var defaultLanguage; final StringBuffer output = StringBuffer(); - try { - input = json.decode(await buildStep.readAsString(buildStep.inputId)); - projectId = input["nstack_project_id"]; // TODO: Validate. - apiKey = input["nstack_api_key"]; // TODO: Validate. + // Read nstack.json file + final Map input = + json.decode(await buildStep.readAsString(buildStep.inputId)); - if(projectId.isEmpty) { - print("ERROR in nstack.json -> 'nstack_project_id' not set"); - return; - } + final String projectId = input['nstack_project_id']; // TODO: Validate. + final String apiKey = input['nstack_api_key']; // TODO: Validate. - if(apiKey.isEmpty) { - print("ERROR in nstack.json -> 'nstack_api_key' not set"); - return; - } + throwIf(projectId.isNullOrBlank, + () => ArgumentError('"nstack_project_id" not set')); + throwIf(apiKey.isNullOrBlank, + () => ArgumentError('"nstack_api_key" not set')); - repository.updateHeaders(projectId, apiKey); - } catch (e, s) { - print(e); - print(s); - } + final config = NStackConfig( + projectId: projectId, + apiKey: apiKey, + ); + + final repository = NStackRepository(config); _writeHeader(output); try { final languages = await repository.fetchAvailableLanguages(); - // Find default - defaultLanguage = languages.where((element) => element['language']['is_default'] == true).first; - print("Found defaultLanguage: " + defaultLanguage['language'].toString()); + // Find the default language + LocalizeIndex defaultLanguage = + languages + .where((it) => it.language.isDefault == true) + .first; + print('Found the default Language: ${defaultLanguage.language}'); // Fetch localization for default language - print("Fetching default localization from: " + defaultLanguage['url'].toString()); - final defaultLanguageJson = await repository.fetchLocalizationForLanguage(defaultLanguage); + print('Fetching default localization from: ${defaultLanguage.url}'); + final localizationResponse = + await repository.fetchLocalizationForLanguage(defaultLanguage); + final localizationData = + LocalizationData.fromJson(jsonDecode(localizationResponse)); - print("Creating Localization class..."); - _writeLocalization(defaultLanguageJson, output); + print('Creating Localization class...'); + _writeLocalization(localizationData, output); - print("Creating NStack section classes..."); - _writeSections(defaultLanguageJson, output); + print('Creating NStack section classes...'); + _writeSections(localizationData, output); - print("Creating NStack..."); + print('Creating NStack...'); _writeNStackConfig(projectId, apiKey, output); _writeLanguageList(languages, output); await _writeBundledTranslations(languages, repository, output); _writeNStack(output); - print("Creating NStackWidget..."); + print('Creating NStackWidget...'); _writeNStackWidget(output); } catch (e, s) { print(e); @@ -76,54 +81,65 @@ class NstackBuilder implements Builder { } void _writeHeader(StringBuffer output) { - output.writeln("// Generated by NStack, do not modify this file."); - output.writeln("import 'package:flutter/widgets.dart';"); - output.writeln("import 'package:nstack/models/language.dart';"); - output.writeln("import 'package:nstack/models/nstack_config.dart';"); - output.writeln("import 'package:nstack/models/section_key.dart';"); - output.writeln("import 'package:nstack/nstack.dart';"); - output.writeln("import 'package:nstack/partial/section_key_delegate.dart';"); - output.writeln(""); - output.writeln("// Update this file by running:"); - output.writeln("// - `flutter pub pub run build_runner build`, if your package depends on Flutter"); - output.writeln("// - `pub run build_runner build` otherwise"); - output.writeln(""); + output.writeln(''' +/// Generated by NStack, do not modify this file. + +import 'package:flutter/widgets.dart'; +import 'package:nstack/models/language.dart'; +import 'package:nstack/models/nstack_config.dart'; +import 'package:nstack/nstack.dart'; +import 'package:nstack/partial/section_key_delegate.dart'; + +// Update this file by running: +// - `flutter pub run build_runner build`, if your package depends on Flutter +// - `pub run build_runner build` otherwise + '''); } - void _writeLocalization(String response, StringBuffer output) { - var languageJson = json.decode(response)['data']; + void _writeLocalization(LocalizationData localization, StringBuffer output) { + final languageJson = localization.data; // Localization class - output.writeln("class Localization {"); + output.writeln('class Localization {'); // Create section fields languageJson.forEach((section, keys) { // Default is a keyword - if(section == "default") section = "defaultSection"; - var classSection = section.toString().replaceRange(0, 1, section.toString().substring(0, 1).toUpperCase()); + if (section == 'default') section = 'defaultSection'; + final classSection = section + .toString() + .replaceRange(0, 1, section.toString().substring(0, 1).toUpperCase()); - output.writeln("\t_$classSection _$section = _$classSection();"); - output.writeln("\t_$classSection get $section => _$section;"); + output.writeln('\tfinal $section = const _$classSection();'); }); - + output.writeln(''); + output.writeln('\tconst Localization();'); output.writeln(''' } '''); } - void _writeSections(String response, StringBuffer output) { - var languageJson = json.decode(response)['data']; - languageJson.forEach((section, keys) { - var rawSection = section; - if(section == "default") section = "defaultSection"; - - var uppercaseSection = section.toString().replaceRange(0, 1, section.toString().substring(0, 1).toUpperCase()); - output.writeln("class _$uppercaseSection extends SectionKeyDelegate {"); - - // Actual String key = "value"; - keys.forEach((k, v) { - //output.writeln("\tString _$k = \"$v\";"); - output.writeln("\tString get $k => get(SectionKey(\"$rawSection\", \"$k\", \"$v\"));"); + void _writeSections(LocalizationData localization, StringBuffer output) { + final languageJson = localization.data; + languageJson.forEach((sectionKey, keys) { + // Check if the section key is using a reserved keyword + String adjustedSectionKey = DartKeywords.isReserved(sectionKey) + // Append 'Section' to the name of the original sectionKey + ? '${sectionKey}Section' + // Use the original sectionKey + : sectionKey; + + final camelCaseClassName = adjustedSectionKey.replaceRange( + 0, 1, sectionKey.substring(0, 1).toUpperCase()); + output.writeln('class _$camelCaseClassName extends SectionKeyDelegate {'); + output.writeln('\tconst _$camelCaseClassName(): super(\'$sectionKey\');'); + output.writeln(''); + + // Actual String key = 'value'; + keys.forEach((stringKey, stringValue) { + //output.writeln('\tString _$k = \'$v\';'); + output.writeln( + '\tString get $stringKey => get(\'$stringKey\', \'$stringValue\');'); }); output.writeln(''' } @@ -131,18 +147,24 @@ class NstackBuilder implements Builder { }); } - void _writeNStackConfig(String projectId, String apiKey, StringBuffer output) { + void _writeNStackConfig(String projectId, + String apiKey, + StringBuffer output,) { output.writeln(''' -final _config = NStackConfig(projectId: "$projectId", apiKey: "$apiKey"); +const _config = NStackConfig(projectId: '$projectId', apiKey: '$apiKey'); '''); } - void _writeLanguageList(dynamic languages, StringBuffer output) { - output.writeln("final _languages = ["); + void _writeLanguageList(List languages, StringBuffer output) { + output.writeln('const _languages = ['); - languages.forEach((v) { - var language = v['language']; - output.writeln("\tLanguage(id: ${language['id']}, locale: \"${language['locale']}\", direction: \"${language['direction']}\", isDefault: ${language['is_default']}, isBestFit: ${language['is_best_fit']}),"); + languages.forEach((localizeIndex) { + Language language = localizeIndex.language; + output.writeln( + '\tLanguage(id: ${language.id}, locale: \'${language + .locale}\', direction: \'${language + .direction}\', isDefault: ${language.isDefault}, isBestFit: ${language + .isBestFit}),'); }); output.writeln(''' @@ -150,14 +172,17 @@ final _config = NStackConfig(projectId: "$projectId", apiKey: "$apiKey"); '''); } - Future _writeBundledTranslations(dynamic languages, NStackRepository repository, StringBuffer output) async { + Future _writeBundledTranslations(List languages, + NStackRepository repository, + StringBuffer output,) async { output.writeln(''' -final _bundledTranslations = {'''); +const _bundledTranslations = {'''); - await Future.forEach(languages, (element) async { - var locale = element['language']['locale']; - var content = (await repository.fetchLocalizationForLanguage(element)); - output.writeln("\t'$locale': '$content',"); + await Future.forEach(languages, (localizeIndex) async { + final locale = localizeIndex.language.locale; + final content = + (await repository.fetchLocalizationForLanguage(localizeIndex)); + output.writeln('\t\'$locale\': \'$content\','); }); output.writeln(''' @@ -169,9 +194,9 @@ final _bundledTranslations = {'''); output.writeln(''' final _nstack = NStack( config: _config, - localization: Localization(), + localization: const Localization(), availableLanguages: _languages, - bundledTranslations: _bundledTranslations + bundledTranslations: _bundledTranslations, ); '''); } @@ -193,16 +218,6 @@ class NStackWidget extends InheritedWidget { nstack != oldWidget.nstack; } -/// Allows to access the Nstack Localization using the BuildContext -extension NStackWidgetExtension on BuildContext { - Localization get localization => NStackWidget.of(this).localization; -} - -/// Allows to access the Nstack Localization from StatefulWidget's State -extension NStackStateExtension on State { - Localization get localization => context.localization; -} - class NStackInitWidget extends StatefulWidget { final Widget child; @@ -232,9 +247,22 @@ class _NStackInitState extends State { return widget.child; } } + +/// Allows to access the Nstack Localization using the BuildContext +extension NStackWidgetExtension on BuildContext { + Localization get localization => NStackWidget.of(this).localization; +} + +/// Allows to access the Nstack Localization from StatefulWidget's State +extension NStackStateExtension on State { + Localization get localization => context.localization; +} '''); } @override - Map> get buildExtensions => const {'.json': ['.dart']}; + Map> get buildExtensions => + const { + '.json': ['.dart'] + }; } diff --git a/lib/src/nstack_repository.dart b/lib/src/nstack_repository.dart index 47431f4..dedc7d2 100644 --- a/lib/src/nstack_repository.dart +++ b/lib/src/nstack_repository.dart @@ -1,90 +1,77 @@ import 'dart:convert'; import 'package:http/http.dart' as http; +import 'package:nstack/models/localize_index.dart'; import 'package:nstack/models/nstack_appopen_data.dart'; +import 'package:nstack/models/nstack_config.dart'; class NStackRepository { + final _baseUrl = 'https://nstack.io/api/v2'; - NStackRepository(); + final NStackConfig _config; - final headers = { - 'Accept-Language' : 'en-US', - 'X-Application-Id' : '', - 'X-Rest-Api-Key' : '', - 'N-Meta' : 'android;local;1.0;1.0;nstackbuilder' - }; + Map get _headers => { + 'Accept-Language': 'en-US', + 'X-Application-Id': _config.projectId, + 'X-Rest-Api-Key': _config.apiKey, + 'N-Meta': 'android;local;1.0;1.0;nstackbuilder' + }; - final baseUrl = "https://nstack.io/api/v2"; + const NStackRepository(this._config); - void updateHeaders( - String appId, - String apiKey - ) { - headers['X-Application-Id'] = appId; - headers['X-Rest-Api-Key'] = apiKey; - } + dynamic postAppOpen({ + String acceptHeader, + NStackAppOpenData appOpenData, + bool devMode, + bool testMode, + }) async { + _headers['Accept-Language'] = acceptHeader; - dynamic postAppOpen({ - String acceptHeader, - NStackAppOpenData appOpenData, - bool devMode, - bool testMode - }) async { - headers['Accept-Language'] = acceptHeader; + final requestBody = { + 'platform': appOpenData.platform, + 'guid': appOpenData.guid, + 'version': appOpenData.version, + 'old_version': appOpenData.oldVersion, + 'last_updated': appOpenData.lastUpdated + }; - var requestBody = { - 'platform': appOpenData.platform, - 'guid': appOpenData.guid, - 'version': appOpenData.version, - 'old_version': appOpenData.oldVersion, - 'last_updated': appOpenData.lastUpdated - }; + print('NStack --> App Open sending: ${requestBody.toString()}'); - try { - print("NStack --> App Open sending: " + requestBody.toString()); + final appOpenResponse = await http.post( + '$_baseUrl/open?dev=$devMode&test=$testMode', + headers: _headers, + body: requestBody, + ); - var appOpenResponse = await http.post( - "$baseUrl/open?dev=$devMode&test=$testMode", - headers: headers, - body: requestBody, - ); + if (appOpenResponse.statusCode == 200) { + print('NStack --> App Open fetched: ${appOpenResponse.body}'); + return json.decode(appOpenResponse.body); + } else { + print( + 'NStack --> App Open failed: ${appOpenResponse.reasonPhrase} - ${appOpenResponse.body} - ${requestBody.toString()}'); + } + } + Future> fetchAvailableLanguages() async { + try { + final response = await http.get( + '$_baseUrl/content/localize/resources/platforms/mobile?dev=true', + headers: _headers, + ); + final Map languagesJson = json.decode(response.body); + final List languagesList = + (languagesJson['data'] as List) + .map((it) => LocalizeIndex.fromJson(it)) + .toList(); + print('Fetched ${languagesList.length} languages.'); + return languagesList; + } catch (e, s) { + print(e); + print(s); + return []; + } + } - if(appOpenResponse.statusCode == 200) { - print("NStack --> App Open fetched: " + appOpenResponse.body); - return json.decode(appOpenResponse.body); - } else { - print("NStack --> App Open failed: " + appOpenResponse.reasonPhrase + " - " + appOpenResponse.body + " - " + requestBody.toString()); - } - } catch (e, s) { - print(e); - print(s); - } - } - - dynamic fetchAvailableLanguages() async { - try { - var languagesResponse = await http.get( - "$baseUrl/content/localize/resources/platforms/mobile?dev=true", - headers: headers - ); - var languages = json.decode(languagesResponse.body); - print("Fetched " + languages['data'].length.toString() + " languages..."); - - return languages['data']; - } catch (e, s) { - print(e); - print(s); - return json.decode("{\"data\":[]}"); - } - } - - dynamic fetchLocalizationForLanguage(dynamic language) async { - try { - return (await http.get(language['url'], headers: headers)).body; - } catch(err) { - print(err); - } - return {}; - } - -} \ No newline at end of file + Future fetchLocalizationForLanguage(LocalizeIndex language) async { + return (await http.get(language.url, headers: _headers)).body; + } +} diff --git a/lib/src/repository.dart b/lib/src/repository.dart index 87b2e5f..405375c 100644 --- a/lib/src/repository.dart +++ b/lib/src/repository.dart @@ -1,40 +1,58 @@ -import '../models/language.dart'; -import '../models/section_key.dart'; +import 'package:nstack/models/language.dart'; + import 'dart:convert'; -class Repository { - - // Factory - static final Repository _instance = Repository._internal(); - factory Repository() => _instance; - Repository._internal(); - - // Internal state - dynamic _internalMap; - Map _bundledTranslations; - List _availableLanguages; - Language _pickedLanguage; - - void setupLocalization(Map bundledTranslations, List availableLanguages, String pickedLanguageLocale) { - this._bundledTranslations = bundledTranslations; - this._availableLanguages = availableLanguages; - this._pickedLanguage = availableLanguages.firstWhere((element) => element.locale == pickedLanguageLocale, orElse: () => availableLanguages.firstWhere((element) => element.isDefault)); - - _setupInternalMap(); - } - - void updateLocalization(dynamic localizationJson, String bestFitLocale) { - this._pickedLanguage = _availableLanguages.firstWhere((element) => element.locale == bestFitLocale, orElse: () => this._pickedLanguage); - _internalMap = localizationJson; - } - - _setupInternalMap() { - try { - _internalMap = json.decode(_bundledTranslations[_pickedLanguage.locale])['data']; - } catch(err) { - print("_setupInternalMap() failed --> " + err.toString()); - } - } - - String getSectionKeyValue(SectionKey sectionKey) => _internalMap[sectionKey.sectionName][sectionKey.keyName] ?? sectionKey.fallbackValue; -} \ No newline at end of file +class LocalizationRepository { + // Factory + static final LocalizationRepository _instance = + LocalizationRepository._internal(); + + factory LocalizationRepository() => _instance; + + LocalizationRepository._internal(); + + // Internal state + Map _sectionsMap; + Map _bundledTranslations; + List _availableLanguages; + Language _pickedLanguage; + + void setupLocalization( + Map bundledTranslations, + List availableLanguages, + String pickedLanguageLocale, + ) { + this._bundledTranslations = bundledTranslations; + this._availableLanguages = availableLanguages; + this._pickedLanguage = availableLanguages.firstWhere( + (language) => language.locale == pickedLanguageLocale, + orElse: () => availableLanguages.firstWhere( + (language) => language.isDefault, + ), + ); + + _setupInternalMap(); + } + + void updateLocalization( + Map localizationJson, String bestFitLocale) { + this._pickedLanguage = _availableLanguages.firstWhere( + (element) => element.locale == bestFitLocale, + orElse: () => this._pickedLanguage, + ); + _sectionsMap = localizationJson; + } + + void _setupInternalMap() { + try { + _sectionsMap = + json.decode(_bundledTranslations[_pickedLanguage.locale])['data']; + } catch (err) { + print('_setupInternalMap() failed --> ${err.toString()}'); + } + } + + String getSectionKeyValue(String sectionKey, String textKey, String fallbackText) { + return _sectionsMap[sectionKey][textKey] ?? fallbackText; + } +} diff --git a/test/nstack_test.dart b/test/nstack_test.dart index b3c9d1b..9175885 100644 --- a/test/nstack_test.dart +++ b/test/nstack_test.dart @@ -1,6 +1,4 @@ -import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:nstack/nstack.dart'; void main() { test('testing test', () async {