improvement/;

bugfixes
 added word libraby (~500 words)
This commit is contained in:
Ujwal
2026-02-23 00:23:37 +05:45
parent 354f7413d1
commit fe2d793b93
6 changed files with 563 additions and 9 deletions

View File

@@ -94,9 +94,12 @@ class AboutPage extends StatelessWidget {
}
Future<void> _launchURL(String url) async {
final uri = Uri.parse(url);
if (await canLaunchUrl(uri)) {
final uri = Uri.tryParse(url);
if (uri == null) return;
try {
await launchUrl(uri, mode: LaunchMode.externalApplication);
} catch (_) {
// Ignore if cannot launch
}
}
}

View File

@@ -209,6 +209,12 @@ class _FrictionSliderTileState extends State<_FrictionSliderTile> {
_draftValue = widget.value;
}
@override
void didUpdateWidget(_FrictionSliderTile old) {
super.didUpdateWidget(old);
if (!_pendingConfirm) _draftValue = widget.value;
}
@override
Widget build(BuildContext context) {
final divisions = ((widget.max - widget.min) / widget.divisor).round();

View File

@@ -2,6 +2,7 @@ import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:provider/provider.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:webview_flutter/webview_flutter.dart';
import '../services/session_manager.dart';
import '../services/settings_service.dart';
@@ -29,6 +30,7 @@ class _MainWebViewPageState extends State<MainWebViewPage>
// Watchdog for app-session expiry
Timer? _watchdog;
bool _extensionDialogShown = false;
bool _lastSessionActive = false;
@override
void initState() {
@@ -36,12 +38,28 @@ class _MainWebViewPageState extends State<MainWebViewPage>
WidgetsBinding.instance.addObserver(this);
_initWebView();
_startWatchdog();
// Listen to session changes to update JS context immediately
WidgetsBinding.instance.addPostFrameCallback((_) {
context.read<SessionManager>().addListener(_onSessionChanged);
_lastSessionActive = context.read<SessionManager>().isSessionActive;
});
}
void _onSessionChanged() {
if (!mounted) return;
final sm = context.read<SessionManager>();
if (_lastSessionActive != sm.isSessionActive) {
_lastSessionActive = sm.isSessionActive;
_applyInjections();
}
}
@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
_watchdog?.cancel();
context.read<SessionManager>().removeListener(_onSessionChanged);
super.dispose();
}
@@ -154,6 +172,15 @@ class _MainWebViewPageState extends State<MainWebViewPage>
);
},
onNavigationRequest: (request) {
// Handle external links (non-Instagram/Facebook)
final uri = Uri.tryParse(request.url);
if (uri != null &&
!uri.host.contains('instagram.com') &&
!uri.host.contains('facebook.com')) {
launchUrl(uri, mode: LaunchMode.externalApplication);
return NavigationDecision.prevent;
}
final decision = NavigationGuard.evaluate(url: request.url);
if (decision.blocked) {
@@ -255,7 +282,11 @@ class _MainWebViewPageState extends State<MainWebViewPage>
await _navigateTo('/explore/');
break;
case 2:
_openSessionModal();
if (context.read<SessionManager>().isSessionActive) {
await _navigateTo('/reels/');
} else {
_openSessionModal();
}
break;
case 3:
await _navigateTo('/direct/inbox/');
@@ -393,11 +424,21 @@ class _EdgePanelState extends State<_EdgePanel> {
// Hits will pass through the Stack to the WebView except on our children.
return Stack(
children: [
// ── Tap-to-close Backdrop (only when expanded) ──
if (_isExpanded)
Positioned.fill(
child: GestureDetector(
onTap: _toggleExpansion,
behavior: HitTestBehavior.opaque,
child: Container(color: Colors.black.withValues(alpha: 0.15)),
),
),
// ── The Handle (Minimized State) ──
if (!_isExpanded)
Positioned(
left: 0,
top: MediaQuery.of(context).size.height * 0.35,
top: MediaQuery.of(context).size.height * 0.35 + 30, // Added margin
child: Material(
color: Colors.transparent,
child: Column(
@@ -480,7 +521,7 @@ class _EdgePanelState extends State<_EdgePanel> {
duration: const Duration(milliseconds: 350),
curve: Curves.easeOutQuart,
left: _isExpanded ? 0 : -220,
top: MediaQuery.of(context).size.height * 0.25,
top: MediaQuery.of(context).size.height * 0.25 + 30, // Added margin
child: GestureDetector(
onHorizontalDragUpdate: (details) {
if (details.delta.dx < -10) _toggleExpansion();

View File

@@ -10,7 +10,7 @@ class SettingsPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
final settings = context.watch<SettingsService>();
// Watching services ensures the UI rebuilds when settings or session state change.
final sm = context.watch<SessionManager>();
return Scaffold(
@@ -45,7 +45,7 @@ class SettingsPage extends StatelessWidget {
title: 'Distraction Management',
subtitle: 'Blur explore and reel controls',
icon: Icons.visibility_off_outlined,
destination: _DistractionSettingsPage(settings: settings),
destination: const _DistractionSettingsPage(),
),
_buildSettingsTile(
context: context,
@@ -149,11 +149,11 @@ class SettingsPage extends StatelessWidget {
}
class _DistractionSettingsPage extends StatelessWidget {
final SettingsService settings;
const _DistractionSettingsPage({required this.settings});
const _DistractionSettingsPage();
@override
Widget build(BuildContext context) {
final settings = context.watch<SettingsService>();
return Scaffold(
backgroundColor: Colors.black,
appBar: AppBar(

View File

@@ -278,6 +278,10 @@ class InjectionController {
(function() {
function lockReelScroll(reelContainer) {
if (reelContainer.dataset.scrollLocked) return;
// If session is active, don't lock
if (window.__focusgramSessionActive === true) return;
reelContainer.dataset.scrollLocked = 'true';
let startY = 0;
@@ -321,6 +325,7 @@ class InjectionController {
/// JS to disable swipe-to-next behavior inside the isolated Reel player.
static const String disableReelSwipeJS = '''
(function disableSwipeNavigation() {
if (window.__focusgramSessionActive === true) return;
let startX = 0;
document.addEventListener('touchstart', e => { startX = e.touches[0].clientX; }, {passive: true});
document.addEventListener('touchmove', e => {
@@ -330,6 +335,10 @@ class InjectionController {
})();
''';
/// JS to update the session state variable in the page context.
static String buildSessionStateJS(bool active) =>
'window.__focusgramSessionActive = $active;';
// ── Public API ──────────────────────────────────────────────────────────────
/// Full injection JS to run on every page load.
@@ -345,9 +354,11 @@ class InjectionController {
if (blurExplore) css.write(_blurExploreCSS);
return '''
${buildSessionStateJS(sessionActive)}
${_buildMutationObserver(css.toString())}
$_periodicNavRemoverJS
$_dismissAppBannerJS
$reelsMutationObserverJS
''';
}
}

View File

@@ -20,6 +20,499 @@ class DisciplineChallenge {
'patience',
'intent',
'choice',
'calm',
'peace',
'truth',
'grit',
'work',
'time',
'life',
'soul',
'mind',
'body',
'heart',
'will',
'hope',
'love',
'kind',
'help',
'give',
'take',
'stay',
'move',
'jump',
'run',
'walk',
'talk',
'sing',
'play',
'read',
'write',
'draw',
'cook',
'bake',
'farm',
'fish',
'hunt',
'grow',
'learn',
'teach',
'lead',
'follow',
'build',
'fix',
'save',
'spend',
'win',
'lose',
'try',
'fail',
'rise',
'fall',
'start',
'stop',
'wait',
'now',
'here',
'slow',
'fast',
'high',
'low',
'deep',
'wide',
'long',
'short',
'big',
'small',
'old',
'new',
'good',
'bad',
'real',
'fake',
'pure',
'dark',
'light',
'fire',
'water',
'earth',
'air',
'wind',
'storm',
'sun',
'moon',
'star',
'sky',
'road',
'path',
'gate',
'door',
'room',
'house',
'home',
'city',
'town',
'land',
'sea',
'ocean',
'lake',
'river',
'wood',
'tree',
'leaf',
'root',
'seed',
'fruit',
'food',
'bread',
'cake',
'milk',
'wine',
'beer',
'salt',
'gold',
'iron',
'rock',
'sand',
'dust',
'bone',
'blood',
'skin',
'hair',
'eyes',
'ears',
'nose',
'mouth',
'hand',
'foot',
'wing',
'tail',
'bird',
'cat',
'dog',
'bear',
'wolf',
'lion',
'deer',
'horse',
'cow',
'pig',
'sheep',
'goat',
'bee',
'ant',
'fly',
'worm',
'snake',
'frog',
'turtle',
'crab',
'whale',
'shark',
'dolphin',
'eagle',
'hawk',
'owl',
'swan',
'duck',
'goose',
'rose',
'lily',
'pine',
'oak',
'fern',
'moss',
'weed',
'grass',
'corn',
'rice',
'bean',
'pea',
'nut',
'oil',
'honey',
'wax',
'silk',
'wool',
'flax',
'hemp',
'paper',
'ink',
'pen',
'book',
'lamp',
'bed',
'chair',
'desk',
'wall',
'roof',
'step',
'mile',
'inch',
'yard',
'hour',
'day',
'week',
'month',
'year',
'age',
'past',
'next',
'death',
'birth',
'name',
'word',
'song',
'poem',
'story',
'myth',
'fact',
'law',
'rule',
'king',
'queen',
'lord',
'lady',
'man',
'woman',
'child',
'youth',
'elder',
'friend',
'foe',
'guest',
'host',
'war',
'fight',
'deal',
'buy',
'sell',
'pay',
'debt',
'cost',
'coin',
'cash',
'bank',
'shop',
'mall',
'mill',
'port',
'ship',
'boat',
'car',
'bus',
'bike',
'train',
'plane',
'street',
'hill',
'peak',
'cave',
'well',
'bridge',
'tower',
'fort',
'camp',
'tent',
'ash',
'smoke',
'coal',
'log',
'branch',
'stick',
'tool',
'hammer',
'nail',
'saw',
'knife',
'fork',
'spoon',
'bowl',
'cup',
'plate',
'pot',
'pan',
'stove',
'oven',
'sink',
'bath',
'soap',
'towel',
'comb',
'brush',
'mirror',
'clock',
'watch',
'ring',
'belt',
'boot',
'shoe',
'sock',
'hat',
'coat',
'shirt',
'pant',
'dress',
'skirt',
'bag',
'box',
'case',
'lock',
'key',
'bell',
'horn',
'drum',
'pipe',
'lute',
'harp',
'flute',
'reed',
'cord',
'rope',
'knot',
'net',
'hook',
'line',
'bait',
'trap',
'plow',
'hoe',
'rake',
'spade',
'crop',
'wheat',
'oats',
'rye',
'zinc',
'lead',
'copper',
'tin',
'silver',
'stone',
'clay',
'mud',
'rain',
'snow',
'hail',
'mist',
'fog',
'cloud',
'dawn',
'dusk',
'noon',
'night',
'ghost',
'angel',
'devil',
'god',
'fate',
'doom',
'fear',
'joy',
'woe',
'pain',
'care',
'hate',
'rage',
'lust',
'greed',
'pride',
'envy',
'sloth',
'wrath',
'holy',
'sin',
'hell',
'heaven',
'void',
'space',
'form',
'idea',
'thought',
'dot',
'circle',
'square',
'point',
'edge',
'flow',
'wave',
'tide',
'shore',
'bank',
'cliff',
'vale',
'meadow',
'field',
'plain',
'desert',
'forest',
'jungle',
'swamp',
'marsh',
'glade',
'grove',
'peak',
'ridge',
'slope',
'track',
'trail',
'lane',
'path',
'alley',
'court',
'plaza',
'park',
'bridge',
'tunnel',
'arch',
'dome',
'spire',
'tower',
'wall',
'fence',
'gate',
'stair',
'floor',
'beam',
'pole',
'mast',
'sail',
'deck',
'hull',
'keel',
'oar',
'helm',
'anchor',
'net',
'rope',
'knot',
'line',
'hook',
'bait',
'trap',
'net',
'spear',
'bow',
'arrow',
'sword',
'shield',
'armor',
'helm',
'boot',
'glove',
'cloak',
'scarf',
'belt',
'ujwal',
'ring',
'gem',
'jewel',
'pearl',
'gold',
'silver',
'bronze',
'iron',
'steel',
'brass',
'glass',
'stone',
'brick',
'tile',
'wood',
'clay',
'wax',
'ink',
'paint',
'dye',
'glue',
'oil',
'coal',
'gas',
'steam',
'heat',
'cold',
'ice',
'frost',
'thaw',
'melt',
'burn',
'glow',
'shine',
'beam',
'ray',
'spark',
'flash',
'flare',
'blast',
'shock',
'wave',
'pulse',
'beat',
'rhythm',
'tone',
'note',
'tune',
'song',
];
/// Shows the word challenge dialog. Returns true if successful.