rothzerg 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (341) hide show
  1. package/dist/cli.js +949 -0
  2. package/package.json +27 -0
  3. package/readme.md +0 -0
  4. package/templates/_base-website/App.tsx +59 -0
  5. package/templates/_base-website/InitialDataContext.tsx +21 -0
  6. package/templates/_base-website/_gitignore +5 -0
  7. package/templates/_base-website/client.tsx +26 -0
  8. package/templates/_base-website/components/DeepLinkLayout.css +41 -0
  9. package/templates/_base-website/components/DeepLinkLayout.tsx +39 -0
  10. package/templates/_base-website/components/DownloadSection.css +60 -0
  11. package/templates/_base-website/components/DownloadSection.tsx +61 -0
  12. package/templates/_base-website/components/SiteFeaturesSection.tsx +68 -0
  13. package/templates/_base-website/components/SiteFooter.tsx +29 -0
  14. package/templates/_base-website/components/SiteHeroSection.tsx +30 -0
  15. package/templates/_base-website/components/SiteHighlightsSection.tsx +38 -0
  16. package/templates/_base-website/components/SiteLanguageSwitcher.tsx +9 -0
  17. package/templates/_base-website/components/SiteNavigation.tsx +24 -0
  18. package/templates/_base-website/config.ts +78 -0
  19. package/templates/_base-website/data/blog/authors.json +18 -0
  20. package/templates/_base-website/data/blog/categories.json +53 -0
  21. package/templates/_base-website/data/blog/posts.json +29 -0
  22. package/templates/_base-website/data/blog/tags.json +86 -0
  23. package/templates/_base-website/i18n/config.ts +60 -0
  24. package/templates/_base-website/i18n/sections/blog/_index.ts +41 -0
  25. package/templates/_base-website/i18n/sections/blog/ar.ts +24 -0
  26. package/templates/_base-website/i18n/sections/blog/de.ts +24 -0
  27. package/templates/_base-website/i18n/sections/blog/en.ts +24 -0
  28. package/templates/_base-website/i18n/sections/blog/es.ts +24 -0
  29. package/templates/_base-website/i18n/sections/blog/fr.ts +24 -0
  30. package/templates/_base-website/i18n/sections/blog/hi.ts +24 -0
  31. package/templates/_base-website/i18n/sections/blog/id.ts +24 -0
  32. package/templates/_base-website/i18n/sections/blog/it.ts +24 -0
  33. package/templates/_base-website/i18n/sections/blog/ja.ts +24 -0
  34. package/templates/_base-website/i18n/sections/blog/ko.ts +24 -0
  35. package/templates/_base-website/i18n/sections/blog/nl.ts +24 -0
  36. package/templates/_base-website/i18n/sections/blog/pl.ts +24 -0
  37. package/templates/_base-website/i18n/sections/blog/pt.ts +24 -0
  38. package/templates/_base-website/i18n/sections/blog/ru.ts +24 -0
  39. package/templates/_base-website/i18n/sections/blog/sv.ts +24 -0
  40. package/templates/_base-website/i18n/sections/blog/th.ts +24 -0
  41. package/templates/_base-website/i18n/sections/blog/tr.ts +24 -0
  42. package/templates/_base-website/i18n/sections/blog/vi.ts +24 -0
  43. package/templates/_base-website/i18n/sections/blog/zh.ts +24 -0
  44. package/templates/_base-website/i18n/sections/common/_index.ts +41 -0
  45. package/templates/_base-website/i18n/sections/common/ar.ts +29 -0
  46. package/templates/_base-website/i18n/sections/common/de.ts +29 -0
  47. package/templates/_base-website/i18n/sections/common/en.ts +29 -0
  48. package/templates/_base-website/i18n/sections/common/es.ts +29 -0
  49. package/templates/_base-website/i18n/sections/common/fr.ts +29 -0
  50. package/templates/_base-website/i18n/sections/common/hi.ts +29 -0
  51. package/templates/_base-website/i18n/sections/common/id.ts +29 -0
  52. package/templates/_base-website/i18n/sections/common/it.ts +29 -0
  53. package/templates/_base-website/i18n/sections/common/ja.ts +29 -0
  54. package/templates/_base-website/i18n/sections/common/ko.ts +29 -0
  55. package/templates/_base-website/i18n/sections/common/nl.ts +29 -0
  56. package/templates/_base-website/i18n/sections/common/pl.ts +29 -0
  57. package/templates/_base-website/i18n/sections/common/pt.ts +29 -0
  58. package/templates/_base-website/i18n/sections/common/ru.ts +29 -0
  59. package/templates/_base-website/i18n/sections/common/sv.ts +29 -0
  60. package/templates/_base-website/i18n/sections/common/th.ts +29 -0
  61. package/templates/_base-website/i18n/sections/common/tr.ts +29 -0
  62. package/templates/_base-website/i18n/sections/common/vi.ts +29 -0
  63. package/templates/_base-website/i18n/sections/common/zh.ts +29 -0
  64. package/templates/_base-website/i18n/sections/deepLink/_index.ts +41 -0
  65. package/templates/_base-website/i18n/sections/deepLink/ar.ts +14 -0
  66. package/templates/_base-website/i18n/sections/deepLink/de.ts +14 -0
  67. package/templates/_base-website/i18n/sections/deepLink/en.ts +14 -0
  68. package/templates/_base-website/i18n/sections/deepLink/es.ts +14 -0
  69. package/templates/_base-website/i18n/sections/deepLink/fr.ts +14 -0
  70. package/templates/_base-website/i18n/sections/deepLink/hi.ts +14 -0
  71. package/templates/_base-website/i18n/sections/deepLink/id.ts +14 -0
  72. package/templates/_base-website/i18n/sections/deepLink/it.ts +14 -0
  73. package/templates/_base-website/i18n/sections/deepLink/ja.ts +14 -0
  74. package/templates/_base-website/i18n/sections/deepLink/ko.ts +14 -0
  75. package/templates/_base-website/i18n/sections/deepLink/nl.ts +14 -0
  76. package/templates/_base-website/i18n/sections/deepLink/pl.ts +14 -0
  77. package/templates/_base-website/i18n/sections/deepLink/pt.ts +14 -0
  78. package/templates/_base-website/i18n/sections/deepLink/ru.ts +14 -0
  79. package/templates/_base-website/i18n/sections/deepLink/sv.ts +14 -0
  80. package/templates/_base-website/i18n/sections/deepLink/th.ts +14 -0
  81. package/templates/_base-website/i18n/sections/deepLink/tr.ts +14 -0
  82. package/templates/_base-website/i18n/sections/deepLink/vi.ts +14 -0
  83. package/templates/_base-website/i18n/sections/deepLink/zh.ts +14 -0
  84. package/templates/_base-website/i18n/sections/home/_index.ts +41 -0
  85. package/templates/_base-website/i18n/sections/home/ar.ts +85 -0
  86. package/templates/_base-website/i18n/sections/home/de.ts +85 -0
  87. package/templates/_base-website/i18n/sections/home/en.ts +85 -0
  88. package/templates/_base-website/i18n/sections/home/es.ts +85 -0
  89. package/templates/_base-website/i18n/sections/home/fr.ts +85 -0
  90. package/templates/_base-website/i18n/sections/home/hi.ts +85 -0
  91. package/templates/_base-website/i18n/sections/home/id.ts +85 -0
  92. package/templates/_base-website/i18n/sections/home/it.ts +85 -0
  93. package/templates/_base-website/i18n/sections/home/ja.ts +85 -0
  94. package/templates/_base-website/i18n/sections/home/ko.ts +85 -0
  95. package/templates/_base-website/i18n/sections/home/nl.ts +85 -0
  96. package/templates/_base-website/i18n/sections/home/pl.ts +85 -0
  97. package/templates/_base-website/i18n/sections/home/pt.ts +85 -0
  98. package/templates/_base-website/i18n/sections/home/ru.ts +85 -0
  99. package/templates/_base-website/i18n/sections/home/sv.ts +85 -0
  100. package/templates/_base-website/i18n/sections/home/th.ts +85 -0
  101. package/templates/_base-website/i18n/sections/home/tr.ts +85 -0
  102. package/templates/_base-website/i18n/sections/home/vi.ts +85 -0
  103. package/templates/_base-website/i18n/sections/home/zh.ts +85 -0
  104. package/templates/_base-website/i18n/sections/support/_index.ts +41 -0
  105. package/templates/_base-website/i18n/sections/support/ar.ts +37 -0
  106. package/templates/_base-website/i18n/sections/support/de.ts +37 -0
  107. package/templates/_base-website/i18n/sections/support/en.ts +37 -0
  108. package/templates/_base-website/i18n/sections/support/es.ts +37 -0
  109. package/templates/_base-website/i18n/sections/support/fr.ts +37 -0
  110. package/templates/_base-website/i18n/sections/support/hi.ts +37 -0
  111. package/templates/_base-website/i18n/sections/support/id.ts +37 -0
  112. package/templates/_base-website/i18n/sections/support/it.ts +37 -0
  113. package/templates/_base-website/i18n/sections/support/ja.ts +37 -0
  114. package/templates/_base-website/i18n/sections/support/ko.ts +37 -0
  115. package/templates/_base-website/i18n/sections/support/nl.ts +37 -0
  116. package/templates/_base-website/i18n/sections/support/pl.ts +37 -0
  117. package/templates/_base-website/i18n/sections/support/pt.ts +37 -0
  118. package/templates/_base-website/i18n/sections/support/ru.ts +37 -0
  119. package/templates/_base-website/i18n/sections/support/sv.ts +37 -0
  120. package/templates/_base-website/i18n/sections/support/th.ts +37 -0
  121. package/templates/_base-website/i18n/sections/support/tr.ts +37 -0
  122. package/templates/_base-website/i18n/sections/support/vi.ts +37 -0
  123. package/templates/_base-website/i18n/sections/support/zh.ts +37 -0
  124. package/templates/_base-website/i18n/translations.ts +25 -0
  125. package/templates/_base-website/index.ts +460 -0
  126. package/templates/_base-website/pages/404.tsx +35 -0
  127. package/templates/_base-website/pages/blog/author.tsx +97 -0
  128. package/templates/_base-website/pages/blog/category.tsx +89 -0
  129. package/templates/_base-website/pages/blog/index.tsx +81 -0
  130. package/templates/_base-website/pages/blog/post.tsx +110 -0
  131. package/templates/_base-website/pages/blog/tag.tsx +86 -0
  132. package/templates/_base-website/pages/custom-pages/example.tsx +54 -0
  133. package/templates/_base-website/pages/index.tsx +29 -0
  134. package/templates/_base-website/pages/privacy.tsx +45 -0
  135. package/templates/_base-website/pages/support.tsx +154 -0
  136. package/templates/_base-website/pages/terms.tsx +68 -0
  137. package/templates/_base-website/public/images/16.png +0 -0
  138. package/templates/_base-website/public/images/apple-touch-icon.png +0 -0
  139. package/templates/_base-website/public/images/favicon-32x32.png +0 -0
  140. package/templates/_base-website/public/images/favicon.png +0 -0
  141. package/templates/_base-website/public/images/logo_dark.svg +6 -0
  142. package/templates/_base-website/public/images/logo_light.svg +6 -0
  143. package/templates/_base-website/public/images/og-image.png +0 -0
  144. package/templates/_base-website/public/images/screenshots/ar_dashboard.jpg +0 -0
  145. package/templates/_base-website/public/images/screenshots/de_dashboard.jpg +0 -0
  146. package/templates/_base-website/public/images/screenshots/en_dashboard.jpg +0 -0
  147. package/templates/_base-website/public/images/screenshots/es_dashboard.jpg +0 -0
  148. package/templates/_base-website/public/images/screenshots/fr_dashboard.jpg +0 -0
  149. package/templates/_base-website/public/images/screenshots/hi_dashboard.jpg +0 -0
  150. package/templates/_base-website/public/images/screenshots/id_dashboard.jpg +0 -0
  151. package/templates/_base-website/public/images/screenshots/it_dashboard.jpg +0 -0
  152. package/templates/_base-website/public/images/screenshots/ja_dashboard.jpg +0 -0
  153. package/templates/_base-website/public/images/screenshots/ko_dashboard.jpg +0 -0
  154. package/templates/_base-website/public/images/screenshots/nl_dashboard.jpg +0 -0
  155. package/templates/_base-website/public/images/screenshots/pl_dashboard.jpg +0 -0
  156. package/templates/_base-website/public/images/screenshots/pt_dashboard.jpg +0 -0
  157. package/templates/_base-website/public/images/screenshots/ru_dashboard.jpg +0 -0
  158. package/templates/_base-website/public/images/screenshots/sv_dashboard.jpg +0 -0
  159. package/templates/_base-website/public/images/screenshots/th_dashboard.jpg +0 -0
  160. package/templates/_base-website/public/images/screenshots/tr_dashboard.jpg +0 -0
  161. package/templates/_base-website/public/images/screenshots/vi_dashboard.jpg +0 -0
  162. package/templates/_base-website/public/images/screenshots/zh_dashboard.jpg +0 -0
  163. package/templates/_base-website/rothzerg.template.json +63 -0
  164. package/templates/_base-website/styles/404.css +32 -0
  165. package/templates/_base-website/styles/_app.css +131 -0
  166. package/templates/_base-website/styles/_shared.css +194 -0
  167. package/templates/_base-website/styles/index.css +1 -0
  168. package/templates/_base-website/styles/privacy.css +1 -0
  169. package/templates/_base-website/styles/support.css +148 -0
  170. package/templates/_base-website/styles/terms.css +22 -0
  171. package/templates/mobile-app/_gitignore +46 -0
  172. package/templates/mobile-app/analysis_options.yaml +28 -0
  173. package/templates/mobile-app/android/app/build.gradle.kts +56 -0
  174. package/templates/mobile-app/android/app/google-services.json +29 -0
  175. package/templates/mobile-app/android/app/src/debug/AndroidManifest.xml +7 -0
  176. package/templates/mobile-app/android/app/src/main/AndroidManifest.xml +54 -0
  177. package/templates/mobile-app/android/app/src/main/kotlin/com/example/{{projectNameSnake}}/MainActivity.kt +5 -0
  178. package/templates/mobile-app/android/app/src/main/res/drawable/launch_background.xml +12 -0
  179. package/templates/mobile-app/android/app/src/main/res/drawable-v21/launch_background.xml +12 -0
  180. package/templates/mobile-app/android/app/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
  181. package/templates/mobile-app/android/app/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
  182. package/templates/mobile-app/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
  183. package/templates/mobile-app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
  184. package/templates/mobile-app/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
  185. package/templates/mobile-app/android/app/src/main/res/values/styles.xml +18 -0
  186. package/templates/mobile-app/android/app/src/main/res/values-night/styles.xml +18 -0
  187. package/templates/mobile-app/android/app/src/profile/AndroidManifest.xml +7 -0
  188. package/templates/mobile-app/android/build.gradle.kts +24 -0
  189. package/templates/mobile-app/android/gradle/wrapper/gradle-wrapper.properties +5 -0
  190. package/templates/mobile-app/android/gradle.properties +2 -0
  191. package/templates/mobile-app/android/settings.gradle.kts +31 -0
  192. package/templates/mobile-app/assets/icons/logo.png +0 -0
  193. package/templates/mobile-app/assets/icons/logo_dark.svg +5 -0
  194. package/templates/mobile-app/assets/icons/logo_light.svg +5 -0
  195. package/templates/mobile-app/assets/lottie/tick.json +1 -0
  196. package/templates/mobile-app/devtools_options.yaml +3 -0
  197. package/templates/mobile-app/ios/Flutter/AppFrameworkInfo.plist +26 -0
  198. package/templates/mobile-app/ios/Flutter/Debug.xcconfig +2 -0
  199. package/templates/mobile-app/ios/Flutter/Release.xcconfig +2 -0
  200. package/templates/mobile-app/ios/Podfile +46 -0
  201. package/templates/mobile-app/ios/Podfile.lock +1807 -0
  202. package/templates/mobile-app/ios/Runner/AppDelegate.swift +16 -0
  203. package/templates/mobile-app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json +122 -0
  204. package/templates/mobile-app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png +0 -0
  205. package/templates/mobile-app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png +0 -0
  206. package/templates/mobile-app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png +0 -0
  207. package/templates/mobile-app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png +0 -0
  208. package/templates/mobile-app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png +0 -0
  209. package/templates/mobile-app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png +0 -0
  210. package/templates/mobile-app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png +0 -0
  211. package/templates/mobile-app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png +0 -0
  212. package/templates/mobile-app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png +0 -0
  213. package/templates/mobile-app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png +0 -0
  214. package/templates/mobile-app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png +0 -0
  215. package/templates/mobile-app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png +0 -0
  216. package/templates/mobile-app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png +0 -0
  217. package/templates/mobile-app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png +0 -0
  218. package/templates/mobile-app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png +0 -0
  219. package/templates/mobile-app/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json +23 -0
  220. package/templates/mobile-app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png +0 -0
  221. package/templates/mobile-app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png +0 -0
  222. package/templates/mobile-app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png +0 -0
  223. package/templates/mobile-app/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md +5 -0
  224. package/templates/mobile-app/ios/Runner/Base.lproj/LaunchScreen.storyboard +37 -0
  225. package/templates/mobile-app/ios/Runner/Base.lproj/Main.storyboard +26 -0
  226. package/templates/mobile-app/ios/Runner/GoogleService-Info.plist +30 -0
  227. package/templates/mobile-app/ios/Runner/Info.plist +70 -0
  228. package/templates/mobile-app/ios/Runner/Runner-Bridging-Header.h +1 -0
  229. package/templates/mobile-app/ios/Runner.xcodeproj/project.pbxproj +772 -0
  230. package/templates/mobile-app/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
  231. package/templates/mobile-app/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
  232. package/templates/mobile-app/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +8 -0
  233. package/templates/mobile-app/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +101 -0
  234. package/templates/mobile-app/ios/Runner.xcworkspace/contents.xcworkspacedata +10 -0
  235. package/templates/mobile-app/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
  236. package/templates/mobile-app/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +8 -0
  237. package/templates/mobile-app/ios/RunnerTests/RunnerTests.swift +12 -0
  238. package/templates/mobile-app/l10n.yaml +5 -0
  239. package/templates/mobile-app/lib/core/config/app_config.dart +76 -0
  240. package/templates/mobile-app/lib/core/constants/app_colors.dart +13 -0
  241. package/templates/mobile-app/lib/core/constants/app_constants.dart +123 -0
  242. package/templates/mobile-app/lib/core/constants/countries.dart +199 -0
  243. package/templates/mobile-app/lib/core/constants/keys.dart +46 -0
  244. package/templates/mobile-app/lib/core/constants/languages.dart +51 -0
  245. package/templates/mobile-app/lib/core/services/admin_service.dart +271 -0
  246. package/templates/mobile-app/lib/core/services/analytics_service.dart +296 -0
  247. package/templates/mobile-app/lib/core/services/auth_service.dart +185 -0
  248. package/templates/mobile-app/lib/core/services/device_service.dart +228 -0
  249. package/templates/mobile-app/lib/core/services/firebase_service.dart +490 -0
  250. package/templates/mobile-app/lib/core/services/in_app_messaging_service.dart +166 -0
  251. package/templates/mobile-app/lib/core/services/logging_service.dart +451 -0
  252. package/templates/mobile-app/lib/core/services/messaging_service.dart +460 -0
  253. package/templates/mobile-app/lib/core/services/remote_config_service.dart +167 -0
  254. package/templates/mobile-app/lib/core/services/revenue_cat_service.dart +364 -0
  255. package/templates/mobile-app/lib/core/services/storage_service.dart +313 -0
  256. package/templates/mobile-app/lib/core/theme/dark_theme.dart +295 -0
  257. package/templates/mobile-app/lib/core/theme/light_theme.dart +296 -0
  258. package/templates/mobile-app/lib/core/utils/formatters.dart +236 -0
  259. package/templates/mobile-app/lib/core/utils/image_picker_helper.dart +78 -0
  260. package/templates/mobile-app/lib/core/utils/validators.dart +292 -0
  261. package/templates/mobile-app/lib/firebase_options.dart +19 -0
  262. package/templates/mobile-app/lib/l10n/app_en.arb +1371 -0
  263. package/templates/mobile-app/lib/l10n/app_localizations.dart +2042 -0
  264. package/templates/mobile-app/lib/l10n/app_localizations_en.dart +1033 -0
  265. package/templates/mobile-app/lib/main.dart +152 -0
  266. package/templates/mobile-app/lib/models/device_model.dart +90 -0
  267. package/templates/mobile-app/lib/models/news_banner.dart +53 -0
  268. package/templates/mobile-app/lib/models/user_model.dart +120 -0
  269. package/templates/mobile-app/lib/providers/admin_provider.dart +67 -0
  270. package/templates/mobile-app/lib/providers/auth_provider.dart +328 -0
  271. package/templates/mobile-app/lib/providers/remote_config_provider.dart +81 -0
  272. package/templates/mobile-app/lib/providers/subscription_provider.dart +67 -0
  273. package/templates/mobile-app/lib/providers/theme_provider.dart +51 -0
  274. package/templates/mobile-app/lib/screens/admin/admin_panel_screen.dart +436 -0
  275. package/templates/mobile-app/lib/screens/admin/admin_panel_user_details_screen.dart +942 -0
  276. package/templates/mobile-app/lib/screens/admin/admin_panel_users_screen.dart +344 -0
  277. package/templates/mobile-app/lib/screens/auth/auth_home_screen.dart +266 -0
  278. package/templates/mobile-app/lib/screens/auth/forgot_password_screen.dart +214 -0
  279. package/templates/mobile-app/lib/screens/auth/login_screen.dart +312 -0
  280. package/templates/mobile-app/lib/screens/auth/profile_setup_screen.dart +157 -0
  281. package/templates/mobile-app/lib/screens/auth/register_screen.dart +288 -0
  282. package/templates/mobile-app/lib/screens/developer/developer_panel_screen.dart +436 -0
  283. package/templates/mobile-app/lib/screens/developer/remote_config_screen.dart +315 -0
  284. package/templates/mobile-app/lib/screens/developer/remote_config_test_screen.dart +461 -0
  285. package/templates/mobile-app/lib/screens/home/home_screen.dart +270 -0
  286. package/templates/mobile-app/lib/screens/legal/privacy_policy_screen.dart +63 -0
  287. package/templates/mobile-app/lib/screens/legal/terms_screen.dart +82 -0
  288. package/templates/mobile-app/lib/screens/main_navigation_screen.dart +85 -0
  289. package/templates/mobile-app/lib/screens/premium/subscription_screen.dart +516 -0
  290. package/templates/mobile-app/lib/screens/profile/edit_profile_screen.dart +246 -0
  291. package/templates/mobile-app/lib/screens/settings/about_app_screen.dart +200 -0
  292. package/templates/mobile-app/lib/screens/settings/cache_manager_screen.dart +400 -0
  293. package/templates/mobile-app/lib/screens/settings/change_password_screen.dart +269 -0
  294. package/templates/mobile-app/lib/screens/settings/delete_account_screen.dart +261 -0
  295. package/templates/mobile-app/lib/screens/settings/settings_screen.dart +400 -0
  296. package/templates/mobile-app/lib/screens/splash/splash_screen.dart +125 -0
  297. package/templates/mobile-app/lib/widgets/app_button.dart +255 -0
  298. package/templates/mobile-app/lib/widgets/app_drawer.dart +207 -0
  299. package/templates/mobile-app/lib/widgets/bottom_sheet_selector.dart +79 -0
  300. package/templates/mobile-app/lib/widgets/button_banner.dart +177 -0
  301. package/templates/mobile-app/lib/widgets/country_picker.dart +169 -0
  302. package/templates/mobile-app/lib/widgets/custom_app_bar.dart +30 -0
  303. package/templates/mobile-app/lib/widgets/email_verification_banner.dart +163 -0
  304. package/templates/mobile-app/lib/widgets/empty_screen.dart +42 -0
  305. package/templates/mobile-app/lib/widgets/language_picker.dart +145 -0
  306. package/templates/mobile-app/lib/widgets/news_banner_widget.dart +264 -0
  307. package/templates/mobile-app/lib/widgets/remote_config/remote_config_widgets.dart +294 -0
  308. package/templates/mobile-app/lib/widgets/section_header.dart +23 -0
  309. package/templates/mobile-app/lib/widgets/success_overlay.dart +117 -0
  310. package/templates/mobile-app/lib/widgets/top_notification.dart +263 -0
  311. package/templates/mobile-app/lib/widgets/user_avatar_picker.dart +58 -0
  312. package/templates/mobile-app/pubspec.yaml +97 -0
  313. package/templates/mobile-app/rothzerg.template.json +43 -0
  314. package/templates/web-app/_gitignore +4 -0
  315. package/templates/web-app/index.html +12 -0
  316. package/templates/web-app/package.json +23 -0
  317. package/templates/web-app/rothzerg.template.json +41 -0
  318. package/templates/web-app/src/App.css +6 -0
  319. package/templates/web-app/src/App.tsx +12 -0
  320. package/templates/web-app/src/index.css +14 -0
  321. package/templates/web-app/src/main.tsx +10 -0
  322. package/templates/web-app/tsconfig.app.json +20 -0
  323. package/templates/web-app/tsconfig.json +7 -0
  324. package/templates/web-app/tsconfig.node.json +18 -0
  325. package/templates/web-app/vite.config.ts +6 -0
  326. package/templates/website-blog/overrides/config.ts +76 -0
  327. package/templates/website-blog/overrides/pages/index.tsx +63 -0
  328. package/templates/website-blog/rothzerg.template.json +29 -0
  329. package/templates/website-business/overrides/App.tsx +49 -0
  330. package/templates/website-business/overrides/components/SiteHeroSection.tsx +33 -0
  331. package/templates/website-business/overrides/config.ts +76 -0
  332. package/templates/website-business/overrides/pages/about.tsx +50 -0
  333. package/templates/website-business/overrides/pages/contact.tsx +61 -0
  334. package/templates/website-business/overrides/pages/index.tsx +37 -0
  335. package/templates/website-business/overrides/pages/services.tsx +70 -0
  336. package/templates/website-business/rothzerg.template.json +18 -0
  337. package/templates/website-mobile/overrides/App.tsx +62 -0
  338. package/templates/website-mobile/overrides/components/SiteHeroSection.tsx +18 -0
  339. package/templates/website-mobile/overrides/config.ts +76 -0
  340. package/templates/website-mobile/overrides/pages/index.tsx +34 -0
  341. package/templates/website-mobile/rothzerg.template.json +64 -0
@@ -0,0 +1,460 @@
1
+ import 'package:firebase_messaging/firebase_messaging.dart';
2
+ import 'package:flutter_local_notifications/flutter_local_notifications.dart';
3
+ import 'package:cloud_firestore/cloud_firestore.dart';
4
+ import 'dart:developer' as developer;
5
+ import 'dart:io';
6
+ import 'device_service.dart';
7
+ import 'in_app_messaging_service.dart';
8
+
9
+ /// Top-level function to handle background messages
10
+ @pragma('vm:entry-point')
11
+ Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
12
+ developer.log(
13
+ 'Handling background message: ${message.messageId}',
14
+ name: 'MessagingService',
15
+ );
16
+ }
17
+
18
+ class MessagingService {
19
+ static final MessagingService _instance = MessagingService._internal();
20
+ factory MessagingService() => _instance;
21
+ MessagingService._internal();
22
+
23
+ final FirebaseMessaging _firebaseMessaging = FirebaseMessaging.instance;
24
+ final FlutterLocalNotificationsPlugin _localNotifications =
25
+ FlutterLocalNotificationsPlugin();
26
+ final FirebaseFirestore _firestore = FirebaseFirestore.instance;
27
+
28
+ bool _isInitialized = false;
29
+ String? _fcmToken;
30
+ String? _userId; // Store userId for later use
31
+
32
+ String? get fcmToken => _fcmToken;
33
+
34
+ /// Initialize Firebase Cloud Messaging
35
+ Future<void> initialize({
36
+ String? userId,
37
+ bool requestPermission = false,
38
+ }) async {
39
+ if (_isInitialized) {
40
+ developer.log('FCM already initialized', name: 'MessagingService');
41
+ return;
42
+ }
43
+
44
+ try {
45
+ developer.log('Initializing FCM...', name: 'MessagingService');
46
+
47
+ // Store userId for later use
48
+ _userId = userId;
49
+
50
+ // Request notification permissions only if explicitly requested
51
+ if (requestPermission) {
52
+ await _requestPermission();
53
+ }
54
+
55
+ // Initialize local notifications
56
+ await _initializeLocalNotifications();
57
+
58
+ // Set up background message handler
59
+ FirebaseMessaging.onBackgroundMessage(
60
+ _firebaseMessagingBackgroundHandler,
61
+ );
62
+
63
+ // Get and save FCM token
64
+ await _setupTokenHandling(userId);
65
+
66
+ // Handle foreground messages
67
+ FirebaseMessaging.onMessage.listen(_handleForegroundMessage);
68
+
69
+ // Handle notification taps (when app is in background or terminated)
70
+ FirebaseMessaging.onMessageOpenedApp.listen(_handleMessageOpenedApp);
71
+
72
+ // Check if app was opened from a notification
73
+ final initialMessage = await _firebaseMessaging.getInitialMessage();
74
+ if (initialMessage != null) {
75
+ _handleMessageOpenedApp(initialMessage);
76
+ }
77
+
78
+ _isInitialized = true;
79
+ developer.log('✅ FCM initialized successfully', name: 'MessagingService');
80
+ } catch (e) {
81
+ developer.log(
82
+ '❌ FCM initialization failed: $e',
83
+ name: 'MessagingService',
84
+ );
85
+ }
86
+ }
87
+
88
+ /// Request notification permissions with optional delay
89
+ Future<void> requestPermissionWithDelay({
90
+ Duration delay = const Duration(seconds: 5),
91
+ }) async {
92
+ developer.log(
93
+ 'Will request notification permission in ${delay.inSeconds} seconds...',
94
+ name: 'MessagingService',
95
+ );
96
+ await Future.delayed(delay);
97
+ await _requestPermission();
98
+ }
99
+
100
+ /// Request notification permissions
101
+ Future<void> _requestPermission() async {
102
+ try {
103
+ final settings = await _firebaseMessaging.requestPermission(
104
+ alert: true,
105
+ badge: true,
106
+ sound: true,
107
+ provisional: false,
108
+ announcement: false,
109
+ carPlay: false,
110
+ criticalAlert: false,
111
+ );
112
+
113
+ developer.log(
114
+ 'Notification permission status: ${settings.authorizationStatus}',
115
+ name: 'MessagingService',
116
+ );
117
+
118
+ if (settings.authorizationStatus == AuthorizationStatus.authorized) {
119
+ developer.log('✅ User granted permission', name: 'MessagingService');
120
+ // Get and save FCM token after permission is granted
121
+ await _getAndSaveToken();
122
+ } else if (settings.authorizationStatus ==
123
+ AuthorizationStatus.provisional) {
124
+ developer.log(
125
+ '✅ User granted provisional permission',
126
+ name: 'MessagingService',
127
+ );
128
+ // Get and save FCM token after permission is granted
129
+ await _getAndSaveToken();
130
+ } else {
131
+ developer.log(
132
+ '⚠️ User declined or has not accepted permission',
133
+ name: 'MessagingService',
134
+ );
135
+ }
136
+ } catch (e) {
137
+ developer.log(
138
+ '❌ Permission request failed: $e',
139
+ name: 'MessagingService',
140
+ );
141
+ }
142
+ }
143
+
144
+ /// Initialize local notifications for displaying foreground messages
145
+ Future<void> _initializeLocalNotifications() async {
146
+ const androidSettings = AndroidInitializationSettings(
147
+ '@mipmap/ic_launcher',
148
+ );
149
+ const iosSettings = DarwinInitializationSettings(
150
+ requestAlertPermission: true,
151
+ requestBadgePermission: true,
152
+ requestSoundPermission: true,
153
+ );
154
+
155
+ const initSettings = InitializationSettings(
156
+ android: androidSettings,
157
+ iOS: iosSettings,
158
+ );
159
+
160
+ await _localNotifications.initialize(
161
+ settings: initSettings,
162
+ onDidReceiveNotificationResponse: _onNotificationTapped,
163
+ );
164
+
165
+ // Create notification channel for Android
166
+ if (Platform.isAndroid) {
167
+ const channel = AndroidNotificationChannel(
168
+ 'high_importance_channel', // id
169
+ 'High Importance Notifications', // name
170
+ description: 'This channel is used for important notifications.',
171
+ importance: Importance.high,
172
+ );
173
+
174
+ await _localNotifications
175
+ .resolvePlatformSpecificImplementation<
176
+ AndroidFlutterLocalNotificationsPlugin
177
+ >()
178
+ ?.createNotificationChannel(channel);
179
+ }
180
+ }
181
+
182
+ /// Get and save FCM token (called after permission is granted)
183
+ Future<void> _getAndSaveToken() async {
184
+ try {
185
+ developer.log('Getting FCM token...', name: 'MessagingService');
186
+
187
+ // On iOS, wait a bit for token to be ready after permission
188
+ if (Platform.isIOS) {
189
+ await Future.delayed(const Duration(milliseconds: 500));
190
+ }
191
+
192
+ final token = await _firebaseMessaging.getToken();
193
+
194
+ if (token != null) {
195
+ _fcmToken = token;
196
+ developer.log(
197
+ '📱 FCM Token obtained: $token',
198
+ name: 'MessagingService',
199
+ );
200
+
201
+ // Save to Firestore if userId is available
202
+ if (_userId != null && _userId!.isNotEmpty) {
203
+ await saveFCMToken(_userId!, token);
204
+ }
205
+ } else {
206
+ developer.log('⚠️ FCM token is still null', name: 'MessagingService');
207
+ }
208
+ } catch (e) {
209
+ developer.log('❌ Failed to get FCM token: $e', name: 'MessagingService');
210
+ }
211
+ }
212
+
213
+ /// Set up FCM token handling
214
+ Future<void> _setupTokenHandling(String? userId) async {
215
+ try {
216
+ // On iOS, we need to wait for APNS token before getting FCM token
217
+ if (Platform.isIOS) {
218
+ // Wait a bit for APNS token to be available
219
+ await Future.delayed(const Duration(seconds: 1));
220
+ }
221
+
222
+ // Get the FCM token
223
+ _fcmToken = await _firebaseMessaging.getToken();
224
+
225
+ if (_fcmToken != null) {
226
+ developer.log('📱 FCM Token: $_fcmToken', name: 'MessagingService');
227
+
228
+ // Save token to Firestore if user is logged in
229
+ if (userId != null && userId.isNotEmpty) {
230
+ await saveFCMToken(userId, _fcmToken!);
231
+ }
232
+ } else {
233
+ developer.log('⚠️ FCM token is null', name: 'MessagingService');
234
+
235
+ // On iOS, if token is null, wait for it via token refresh listener
236
+ if (Platform.isIOS) {
237
+ developer.log(
238
+ '⏳ Waiting for APNS token on iOS...',
239
+ name: 'MessagingService',
240
+ );
241
+ }
242
+ }
243
+
244
+ // Listen for token refresh
245
+ _firebaseMessaging.onTokenRefresh.listen((newToken) {
246
+ developer.log(
247
+ '🔄 FCM Token refreshed: $newToken',
248
+ name: 'MessagingService',
249
+ );
250
+ _fcmToken = newToken;
251
+
252
+ // Use stored userId if available
253
+ final currentUserId = userId ?? _userId;
254
+ if (currentUserId != null && currentUserId.isNotEmpty) {
255
+ saveFCMToken(currentUserId, newToken);
256
+ }
257
+ });
258
+ } catch (e) {
259
+ developer.log('❌ Token setup failed: $e', name: 'MessagingService');
260
+
261
+ // Don't fail the entire initialization if token retrieval fails
262
+ // Token will be available via onTokenRefresh listener
263
+ if (Platform.isIOS) {
264
+ developer.log(
265
+ '💡 Token will be available once APNS token is received',
266
+ name: 'MessagingService',
267
+ );
268
+ }
269
+ }
270
+ }
271
+
272
+ /// Save FCM token to Firestore devices collection
273
+ Future<void> saveFCMToken(String userId, String fcmToken) async {
274
+ try {
275
+ // Get device ID from device info
276
+ final deviceId = await _getDeviceId();
277
+
278
+ if (deviceId.isEmpty) {
279
+ developer.log(
280
+ '⚠️ Device ID is empty, skipping FCM token save',
281
+ name: 'MessagingService',
282
+ );
283
+ return;
284
+ }
285
+
286
+ developer.log(
287
+ '💾 Saving FCM token to Firestore...',
288
+ name: 'MessagingService',
289
+ );
290
+
291
+ // Get FIAM installation ID if available
292
+ final inAppMessagingService = InAppMessagingService();
293
+ final installationId = inAppMessagingService.installationId;
294
+
295
+ final data = {
296
+ 'userId': userId,
297
+ 'fcmToken': fcmToken,
298
+ 'fcmTokenUpdatedAt': FieldValue.serverTimestamp(),
299
+ 'lastSeenAt': FieldValue.serverTimestamp(),
300
+ };
301
+
302
+ // Add Firebase installation ID if available
303
+ if (installationId != null) {
304
+ data['installationId'] = installationId;
305
+ data['installationIdUpdatedAt'] = FieldValue.serverTimestamp();
306
+ developer.log(
307
+ '💾 Including Firebase installation ID: ${installationId.substring(0, 8)}...',
308
+ name: 'MessagingService',
309
+ );
310
+ }
311
+
312
+ await _firestore
313
+ .collection('devices')
314
+ .doc(deviceId)
315
+ .set(data, SetOptions(merge: true));
316
+
317
+ developer.log('✅ FCM token saved successfully', name: 'MessagingService');
318
+ } catch (e) {
319
+ developer.log('❌ Failed to save FCM token: $e', name: 'MessagingService');
320
+ }
321
+ }
322
+
323
+ /// Get device ID (reusing logic from DeviceService)
324
+ Future<String> _getDeviceId() async {
325
+ try {
326
+ final deviceService = DeviceService();
327
+ return await deviceService.getDeviceId();
328
+ } catch (e) {
329
+ developer.log('❌ Failed to get device ID: $e', name: 'MessagingService');
330
+ return '';
331
+ }
332
+ }
333
+
334
+ /// Handle foreground messages
335
+ void _handleForegroundMessage(RemoteMessage message) {
336
+ developer.log(
337
+ 'Received foreground message: ${message.messageId}',
338
+ name: 'MessagingService',
339
+ );
340
+
341
+ // Show local notification when app is in foreground
342
+ _showLocalNotification(message);
343
+ }
344
+
345
+ /// Show local notification
346
+ Future<void> _showLocalNotification(RemoteMessage message) async {
347
+ final notification = message.notification;
348
+ final android = message.notification?.android;
349
+
350
+ if (notification != null) {
351
+ await _localNotifications.show(
352
+ id: notification.hashCode,
353
+ title: notification.title,
354
+ body: notification.body,
355
+ notificationDetails: NotificationDetails(
356
+ android: AndroidNotificationDetails(
357
+ 'high_importance_channel',
358
+ 'High Importance Notifications',
359
+ channelDescription:
360
+ 'This channel is used for important notifications.',
361
+ importance: Importance.high,
362
+ priority: Priority.high,
363
+ icon: android?.smallIcon ?? '@mipmap/ic_launcher',
364
+ ),
365
+ iOS: const DarwinNotificationDetails(
366
+ presentAlert: true,
367
+ presentBadge: true,
368
+ presentSound: true,
369
+ ),
370
+ ),
371
+ payload: message.data.toString(),
372
+ );
373
+ }
374
+ }
375
+
376
+ /// Handle notification tap
377
+ void _onNotificationTapped(NotificationResponse response) {
378
+ developer.log(
379
+ 'Notification tapped: ${response.payload}',
380
+ name: 'MessagingService',
381
+ );
382
+ // TODO: Navigate to specific screen based on payload
383
+ }
384
+
385
+ /// Handle message opened app (when app is in background or terminated)
386
+ void _handleMessageOpenedApp(RemoteMessage message) {
387
+ developer.log(
388
+ 'Message opened app: ${message.messageId}',
389
+ name: 'MessagingService',
390
+ );
391
+ // TODO: Navigate to specific screen based on message data
392
+ }
393
+
394
+ /// Subscribe to a topic
395
+ Future<void> subscribeToTopic(String topic) async {
396
+ try {
397
+ await _firebaseMessaging.subscribeToTopic(topic);
398
+ developer.log('✅ Subscribed to topic: $topic', name: 'MessagingService');
399
+ } catch (e) {
400
+ developer.log(
401
+ '❌ Failed to subscribe to topic: $e',
402
+ name: 'MessagingService',
403
+ );
404
+ }
405
+ }
406
+
407
+ /// Unsubscribe from a topic
408
+ Future<void> unsubscribeFromTopic(String topic) async {
409
+ try {
410
+ await _firebaseMessaging.unsubscribeFromTopic(topic);
411
+ developer.log(
412
+ '✅ Unsubscribed from topic: $topic',
413
+ name: 'MessagingService',
414
+ );
415
+ } catch (e) {
416
+ developer.log(
417
+ '❌ Failed to unsubscribe from topic: $e',
418
+ name: 'MessagingService',
419
+ );
420
+ }
421
+ }
422
+
423
+ /// Delete FCM token
424
+ Future<void> deleteToken() async {
425
+ try {
426
+ await _firebaseMessaging.deleteToken();
427
+ _fcmToken = null;
428
+ developer.log('✅ FCM token deleted', name: 'MessagingService');
429
+ } catch (e) {
430
+ developer.log(
431
+ '❌ Failed to delete FCM token: $e',
432
+ name: 'MessagingService',
433
+ );
434
+ }
435
+ }
436
+
437
+ /// Clean up FCM token from Firestore (call on sign out)
438
+ Future<void> removeFCMTokenFromFirestore(String userId) async {
439
+ try {
440
+ final deviceId = await _getDeviceId();
441
+
442
+ if (deviceId.isEmpty) return;
443
+
444
+ await _firestore.collection('devices').doc(deviceId).set({
445
+ 'fcmToken': FieldValue.delete(),
446
+ 'fcmTokenUpdatedAt': FieldValue.delete(),
447
+ }, SetOptions(merge: true));
448
+
449
+ developer.log(
450
+ '✅ FCM token removed from Firestore',
451
+ name: 'MessagingService',
452
+ );
453
+ } catch (e) {
454
+ developer.log(
455
+ '❌ Failed to remove FCM token from Firestore: $e',
456
+ name: 'MessagingService',
457
+ );
458
+ }
459
+ }
460
+ }
@@ -0,0 +1,167 @@
1
+ import 'package:firebase_remote_config/firebase_remote_config.dart';
2
+ import 'dart:developer' as developer;
3
+
4
+ import '../constants/app_constants.dart';
5
+
6
+ /// Service for managing Firebase Remote Config
7
+ /// Provides feature flags and remote configuration
8
+ class RemoteConfigService {
9
+ static final RemoteConfigService _instance = RemoteConfigService._internal();
10
+ factory RemoteConfigService() => _instance;
11
+ RemoteConfigService._internal();
12
+
13
+ final FirebaseRemoteConfig _remoteConfig = FirebaseRemoteConfig.instance;
14
+ bool _initialized = false;
15
+
16
+ /// Initialize Remote Config with default values
17
+ Future<void> initialize() async {
18
+ if (_initialized) return;
19
+
20
+ try {
21
+ // Set config settings
22
+ await _remoteConfig.setConfigSettings(
23
+ RemoteConfigSettings(
24
+ fetchTimeout: AppConstants.remoteConfigFetchTimeout,
25
+ minimumFetchInterval: AppConstants.remoteConfigCacheDuration,
26
+ ),
27
+ );
28
+
29
+ // Set default values
30
+ await _remoteConfig.setDefaults(_defaultValues);
31
+
32
+ // Fetch and activate
33
+ await _remoteConfig.fetchAndActivate();
34
+
35
+ _initialized = true;
36
+ developer.log('RemoteConfigService initialized', name: 'RemoteConfig');
37
+ } catch (e) {
38
+ developer.log(
39
+ 'Failed to initialize RemoteConfig: $e',
40
+ name: 'RemoteConfig',
41
+ error: e,
42
+ );
43
+ // Don't rethrow - app should work with defaults
44
+ }
45
+ }
46
+
47
+ /// Default values for remote config
48
+ static const Map<String, dynamic> _defaultValues = {
49
+ AppConstants.rcEnableGoogleAuth: false,
50
+ AppConstants.rcEnableAppleAuth: false,
51
+ AppConstants.rcEnableAnonymousAuth: true,
52
+ AppConstants.rcMaintenanceMode: false,
53
+ AppConstants.rcMinimumVersion: '1.0.0',
54
+ AppConstants.rcForceUpdate: false,
55
+ };
56
+
57
+ // ============= Feature Flags =============
58
+
59
+ bool get enableGoogleAuth {
60
+ return _remoteConfig.getBool(AppConstants.rcEnableGoogleAuth);
61
+ }
62
+
63
+ bool get enableAppleAuth {
64
+ return _remoteConfig.getBool(AppConstants.rcEnableAppleAuth);
65
+ }
66
+
67
+ bool get enableAnonymousAuth {
68
+ return _remoteConfig.getBool(AppConstants.rcEnableAnonymousAuth);
69
+ }
70
+
71
+ bool get maintenanceMode {
72
+ return _remoteConfig.getBool(AppConstants.rcMaintenanceMode);
73
+ }
74
+
75
+ String get minimumVersion {
76
+ return _remoteConfig.getString(AppConstants.rcMinimumVersion);
77
+ }
78
+
79
+ bool get forceUpdate {
80
+ return _remoteConfig.getBool(AppConstants.rcForceUpdate);
81
+ }
82
+
83
+ // ============= Generic Getters =============
84
+
85
+ bool getBool(String key) {
86
+ return _remoteConfig.getBool(key);
87
+ }
88
+
89
+ String getString(String key) {
90
+ return _remoteConfig.getString(key);
91
+ }
92
+
93
+ int getInt(String key) {
94
+ return _remoteConfig.getInt(key);
95
+ }
96
+
97
+ double getDouble(String key) {
98
+ return _remoteConfig.getDouble(key);
99
+ }
100
+
101
+ // ============= Refresh Config =============
102
+
103
+ Future<bool> fetchConfig() async {
104
+ try {
105
+ final updated = await _remoteConfig.fetchAndActivate();
106
+ developer.log(
107
+ 'Config fetched: ${updated ? "updated" : "no changes"}',
108
+ name: 'RemoteConfig',
109
+ );
110
+ return updated;
111
+ } catch (e) {
112
+ developer.log(
113
+ 'Failed to fetch config: $e',
114
+ name: 'RemoteConfig',
115
+ error: e,
116
+ );
117
+ return false;
118
+ }
119
+ }
120
+
121
+ /// Force fetch config (ignores cache)
122
+ Future<bool> forceFetchConfig() async {
123
+ try {
124
+ await _remoteConfig.setConfigSettings(
125
+ RemoteConfigSettings(
126
+ fetchTimeout: AppConstants.remoteConfigFetchTimeout,
127
+ minimumFetchInterval: Duration.zero,
128
+ ),
129
+ );
130
+ final updated = await _remoteConfig.fetchAndActivate();
131
+
132
+ // Reset to normal fetch interval
133
+ await _remoteConfig.setConfigSettings(
134
+ RemoteConfigSettings(
135
+ fetchTimeout: AppConstants.remoteConfigFetchTimeout,
136
+ minimumFetchInterval: AppConstants.remoteConfigCacheDuration,
137
+ ),
138
+ );
139
+
140
+ developer.log('Config force fetched', name: 'RemoteConfig');
141
+ return updated;
142
+ } catch (e) {
143
+ developer.log(
144
+ 'Failed to force fetch config: $e',
145
+ name: 'RemoteConfig',
146
+ error: e,
147
+ );
148
+ return false;
149
+ }
150
+ }
151
+
152
+ // ============= Utility Methods =============
153
+
154
+ Map<String, dynamic> getAllValues() {
155
+ return _remoteConfig.getAll().map(
156
+ (key, value) => MapEntry(key, value.asString()),
157
+ );
158
+ }
159
+
160
+ DateTime get lastFetchTime {
161
+ return _remoteConfig.lastFetchTime;
162
+ }
163
+
164
+ RemoteConfigFetchStatus get lastFetchStatus {
165
+ return _remoteConfig.lastFetchStatus;
166
+ }
167
+ }