Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions mobile-app/lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -729,6 +729,9 @@
"daily_challenge_start": "Start the challenge",
"daily_challenge_completed_semantics": "Daily challenge completed. View past challenges.",
"daily_challenge_card_semantics": "Daily challenge card",
"daily_challenge_archive_card_semantics": "A year of coding challenges. View the archive.",
"daily_challenge_archive_title": "A year of coding challenges",
"daily_challenge_view_archive": "View challenge archive",
"daily_challenge_month_description": "Explore the daily coding challenges for {monthYear}. Stay motivated and keep your learning streak alive!",
"@daily_challenge_month_description": {
"description": "daily challenges view description for a month group",
Expand Down Expand Up @@ -781,11 +784,11 @@
}
}
},
"new_daily_challenge_available": "New Daily Challenge Available! 🧩",
"new_daily_challenge_available": "Daily Coding Challenge",
"@new_daily_challenge_available": {
"description": "daily challenge notification title"
},
"daily_challenge_notification_body": "A fresh coding challenge is waiting for you. Ready to solve it?",
"daily_challenge_notification_body": "Today's coding challenge is ready. Can you solve it?",
"@daily_challenge_notification_body": {
"description": "daily challenge notification body"
},
Expand Down
7 changes: 5 additions & 2 deletions mobile-app/lib/l10n/app_es.arb
Original file line number Diff line number Diff line change
Expand Up @@ -650,6 +650,9 @@
"daily_challenge_start": "Start the challenge",
"daily_challenge_completed_semantics": "Daily challenge completed. View past challenges.",
"daily_challenge_card_semantics": "Daily challenge card",
"daily_challenge_archive_card_semantics": "A year of coding challenges. View the archive.",
"daily_challenge_archive_title": "A year of coding challenges",
"daily_challenge_view_archive": "View challenge archive",
"daily_challenge_month_description": "Explore the daily coding challenges for {monthYear}. Stay motivated and keep your learning streak alive!",
"@daily_challenge_month_description": {
"placeholders": {
Expand Down Expand Up @@ -691,8 +694,8 @@
}
}
},
"new_daily_challenge_available": "New Daily Challenge Available! 🧩",
"daily_challenge_notification_body": "A fresh coding challenge is waiting for you. Ready to solve it?",
"new_daily_challenge_available": "Daily Coding Challenge",
"daily_challenge_notification_body": "Today's coding challenge is ready. Can you solve it?",
"daily_challenge_notifications": "Daily Challenge Notifications",
"daily_challenge_notifications_description": "Notifications for new daily coding challenges",
"download_complete": "Download complete",
Expand Down
22 changes: 20 additions & 2 deletions mobile-app/lib/l10n/app_localizations.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1205,6 +1205,24 @@ abstract class AppLocalizations {
/// **'Daily challenge card'**
String get daily_challenge_card_semantics;

/// No description provided for @daily_challenge_archive_card_semantics.
///
/// In en, this message translates to:
/// **'A year of coding challenges. View the archive.'**
String get daily_challenge_archive_card_semantics;

/// No description provided for @daily_challenge_archive_title.
///
/// In en, this message translates to:
/// **'A year of coding challenges'**
String get daily_challenge_archive_title;

/// No description provided for @daily_challenge_view_archive.
///
/// In en, this message translates to:
/// **'View challenge archive'**
String get daily_challenge_view_archive;

/// daily challenges view description for a month group
///
/// In en, this message translates to:
Expand Down Expand Up @@ -1234,13 +1252,13 @@ abstract class AppLocalizations {
/// daily challenge notification title
///
/// In en, this message translates to:
/// **'New Daily Challenge Available! 🧩'**
/// **'Daily Coding Challenge'**
String get new_daily_challenge_available;

/// daily challenge notification body
///
/// In en, this message translates to:
/// **'A fresh coding challenge is waiting for you. Ready to solve it?'**
/// **'Today\'s coding challenge is ready. Can you solve it?'**
String get daily_challenge_notification_body;

/// Android notification channel name for daily challenge notifications
Expand Down
15 changes: 12 additions & 3 deletions mobile-app/lib/l10n/app_localizations_en.dart
Original file line number Diff line number Diff line change
Expand Up @@ -651,6 +651,16 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get daily_challenge_card_semantics => 'Daily challenge card';

@override
String get daily_challenge_archive_card_semantics =>
'A year of coding challenges. View the archive.';

@override
String get daily_challenge_archive_title => 'A year of coding challenges';

@override
String get daily_challenge_view_archive => 'View challenge archive';

@override
String daily_challenge_month_description(String monthYear) {
return 'Explore the daily coding challenges for $monthYear. Stay motivated and keep your learning streak alive!';
Expand All @@ -675,12 +685,11 @@ class AppLocalizationsEn extends AppLocalizations {
}

@override
String get new_daily_challenge_available =>
'New Daily Challenge Available! 🧩';
String get new_daily_challenge_available => 'Daily Coding Challenge';

@override
String get daily_challenge_notification_body =>
'A fresh coding challenge is waiting for you. Ready to solve it?';
'Today\'s coding challenge is ready. Can you solve it?';

@override
String get daily_challenge_notifications => 'Daily Challenge Notifications';
Expand Down
15 changes: 12 additions & 3 deletions mobile-app/lib/l10n/app_localizations_es.dart
Original file line number Diff line number Diff line change
Expand Up @@ -651,6 +651,16 @@ class AppLocalizationsEs extends AppLocalizations {
@override
String get daily_challenge_card_semantics => 'Daily challenge card';

@override
String get daily_challenge_archive_card_semantics =>
'A year of coding challenges. View the archive.';

@override
String get daily_challenge_archive_title => 'A year of coding challenges';

@override
String get daily_challenge_view_archive => 'View challenge archive';

@override
String daily_challenge_month_description(String monthYear) {
return 'Explore the daily coding challenges for $monthYear. Stay motivated and keep your learning streak alive!';
Expand All @@ -675,12 +685,11 @@ class AppLocalizationsEs extends AppLocalizations {
}

@override
String get new_daily_challenge_available =>
'New Daily Challenge Available! 🧩';
String get new_daily_challenge_available => 'Daily Coding Challenge';

@override
String get daily_challenge_notification_body =>
'A fresh coding challenge is waiting for you. Ready to solve it?';
'Today\'s coding challenge is ready. Can you solve it?';

@override
String get daily_challenge_notifications => 'Daily Challenge Notifications';
Expand Down
15 changes: 12 additions & 3 deletions mobile-app/lib/l10n/app_localizations_pt.dart
Original file line number Diff line number Diff line change
Expand Up @@ -651,6 +651,16 @@ class AppLocalizationsPt extends AppLocalizations {
@override
String get daily_challenge_card_semantics => 'Daily challenge card';

@override
String get daily_challenge_archive_card_semantics =>
'A year of coding challenges. View the archive.';

@override
String get daily_challenge_archive_title => 'A year of coding challenges';

@override
String get daily_challenge_view_archive => 'View challenge archive';

@override
String daily_challenge_month_description(String monthYear) {
return 'Explore the daily coding challenges for $monthYear. Stay motivated and keep your learning streak alive!';
Expand All @@ -675,12 +685,11 @@ class AppLocalizationsPt extends AppLocalizations {
}

@override
String get new_daily_challenge_available =>
'New Daily Challenge Available! 🧩';
String get new_daily_challenge_available => 'Daily Coding Challenge';

@override
String get daily_challenge_notification_body =>
'A fresh coding challenge is waiting for you. Ready to solve it?';
'Today\'s coding challenge is ready. Can you solve it?';

@override
String get daily_challenge_notifications => 'Daily Challenge Notifications';
Expand Down
7 changes: 5 additions & 2 deletions mobile-app/lib/l10n/app_pt.arb
Original file line number Diff line number Diff line change
Expand Up @@ -650,6 +650,9 @@
"daily_challenge_start": "Start the challenge",
"daily_challenge_completed_semantics": "Daily challenge completed. View past challenges.",
"daily_challenge_card_semantics": "Daily challenge card",
"daily_challenge_archive_card_semantics": "A year of coding challenges. View the archive.",
"daily_challenge_archive_title": "A year of coding challenges",
"daily_challenge_view_archive": "View challenge archive",
"daily_challenge_month_description": "Explore the daily coding challenges for {monthYear}. Stay motivated and keep your learning streak alive!",
"@daily_challenge_month_description": {
"placeholders": {
Expand Down Expand Up @@ -691,8 +694,8 @@
}
}
},
"new_daily_challenge_available": "New Daily Challenge Available! 🧩",
"daily_challenge_notification_body": "A fresh coding challenge is waiting for you. Ready to solve it?",
"new_daily_challenge_available": "Daily Coding Challenge",
"daily_challenge_notification_body": "Today's coding challenge is ready. Can you solve it?",
"daily_challenge_notifications": "Daily Challenge Notifications",
"daily_challenge_notifications_description": "Notifications for new daily coding challenges",
"download_complete": "Download complete",
Expand Down
7 changes: 5 additions & 2 deletions mobile-app/lib/models/learn/daily_challenge_model.dart
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ class DailyChallengeLanguageData {

/// Model representing a block of daily challenges for a specific month
class DailyChallengeBlock {
final String monthYear; // e.g., "January 2025"
final String monthYear; // e.g., "January 2025" — used as an internal key
final List<DailyChallengeOverview> challenges;
final String description;

Expand All @@ -100,11 +100,14 @@ class DailyChallengeBlock {
required this.description,
});

// Year-agnostic display name, e.g. "January"
String get displayMonth => monthYear.split(' ').first;

// Daily challenges don't have a block or a super block.
// Return a `Block` here to allow daily challenges
// to reuse the code for standard challenges.
Block toCurriculumBlock() {
final blockName = 'Daily Challenges $monthYear';
final blockName = 'Daily Challenges $displayMonth';
final blockDashedName =
'daily-challenges-${monthYear.toLowerCase().replaceAll(' ', '-')}';
return Block(
Expand Down
6 changes: 5 additions & 1 deletion mobile-app/lib/service/learn/daily_challenge_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,12 @@ class DailyChallengeService {
}

Future<DailyChallenge> fetchChallengeByDate(String date) async {
// Convert YYYY-MM-DD to MM-DD for the year-agnostic /day/:day endpoint.
// Also accepts an already-converted MM-DD string.
final monthDay =
date.length == 10 ? date.substring(5) : date;
final response = await _dio.get(
'${AuthenticationService.baseApiURL}/daily-coding-challenge/date/$date');
'${AuthenticationService.baseApiURL}/daily-coding-challenge/day/$monthDay');

if (response.statusCode == 200) {
return DailyChallenge.fromJson(response.data);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ class DailyChallengeView extends StatelessWidget {
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
block.monthYear,
block.displayMonth,
style: TextStyle(
wordSpacing: 0,
letterSpacing: 0,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,8 @@ class DailyChallengeViewModel extends BaseViewModel {
final block = DailyChallengeBlock(
monthYear: monthYear,
challenges: monthChallenges,
description: t.daily_challenge_month_description(monthYear),
description: t.daily_challenge_month_description(
monthYear.split(' ').first),
);

_blocks.add(block);
Expand Down Expand Up @@ -132,7 +133,8 @@ class DailyChallengeViewModel extends BaseViewModel {
Block block = DailyChallengeBlock(
monthYear: monthYear,
challenges: [challenge],
description: t.daily_challenge_month_description(monthYear),
description: t.daily_challenge_month_description(
monthYear.split(' ').first),
).toCurriculumBlock();

_navigationService.navigateTo(
Expand Down
76 changes: 76 additions & 0 deletions mobile-app/lib/ui/views/learn/landing/landing_view.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:freecodecamp/app/app.locator.dart';
import 'package:freecodecamp/app/app.router.dart';
import 'package:freecodecamp/extensions/i18n_extension.dart';
import 'package:freecodecamp/models/learn/curriculum_model.dart';
import 'package:freecodecamp/models/learn/motivational_quote_model.dart';
Expand All @@ -9,6 +11,7 @@ import 'package:freecodecamp/ui/views/learn/landing/landing_viewmodel.dart';
import 'package:freecodecamp/ui/views/learn/widgets/daily_challenge_card.dart';
import 'package:freecodecamp/ui/widgets/drawer_widget/drawer_widget_view.dart';
import 'package:stacked/stacked.dart';
import 'package:stacked_services/stacked_services.dart';

class LearnLandingView extends StatelessWidget {
const LearnLandingView({super.key});
Expand Down Expand Up @@ -324,6 +327,79 @@ class SuperBlockButton extends StatelessWidget {
}
}

class DailyChallengeButton extends StatelessWidget {
const DailyChallengeButton({super.key});

@override
Widget build(BuildContext context) {
final navigationService = locator<NavigationService>();

return Container(
padding: const EdgeInsets.symmetric(
vertical: 6,
horizontal: 4,
),
constraints: BoxConstraints(
minHeight: 80,
),
child: ElevatedButton(
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.all(5),
backgroundColor: FccColors.gray80,
side: const BorderSide(
width: 2,
color: FccColors.gray75,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(5)),
side: const BorderSide(
color: Colors.teal,
width: 2.0,
),
),
),
onPressed: () {
navigationService.navigateTo(Routes.dailyChallengeView);
},
child: Row(
children: [
Padding(
padding: const EdgeInsets.all(12.0),
child: SizedBox(
width: 36,
height: 36,
child: Icon(
Icons.calendar_today,
color: FccColors.gray00,
size: 36,
),
),
),
Expanded(
flex: 8,
child: Text(
context.t.daily_challenge_view_archive,
textAlign: TextAlign.left,
style: const TextStyle(fontSize: 20),
),
),
const Expanded(
flex: 2,
child: Align(
alignment: Alignment.centerRight,
child: Padding(
padding: EdgeInsets.symmetric(vertical: 8, horizontal: 12),
child: Icon(Icons.arrow_forward_ios),
),
),
)
],
),
),
);
}
}

class QuoteWidget extends StatelessWidget {
const QuoteWidget({
super.key,
Expand Down
13 changes: 13 additions & 0 deletions mobile-app/lib/ui/views/learn/landing/landing_viewmodel.dart
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,19 @@ class LearnLandingViewModel extends BaseViewModel {

widgetOrder.add(button);
}

// Insert daily challenge archive after core curriculum
if (stage == 'core') {
widgetOrder.add(
Text(
t.daily_challenge_archive_title,
style: headerStyle,
),
);
widgetOrder.add(
const DailyChallengeButton(),
);
}
}
}

Expand Down
Loading