Posts in category “Programming”

C# inline regular expression syntax and usage

I found this brilliant answer at Stack overflow. It is way more clearer and useful than Microsoft's official one

You can use inline modifiers as follows:

// case insensitive match
Regex MyRegex = new Regex(@"(?i)[a-z]+");  // case insensitive match

or, inverse the meaning of the modifier by adding a minus-sign:

// case sensitive match
Regex MyRegex = new Regex(@"(?-i)[a-z]+");  // case sensitive match

or, switch them on and off:

// case sensitive, then case-insensitive match
Regex MyRegex = new Regex(@"(?-i)[a-z]+(?i)[k-n]+");

Alternatively, you can use the mode-modifier span syntax using a colon : and a grouping parenthesis, which scopes the modifier to only that group:

// case sensitive, then case-insensitive match
Regex MyRegex = new Regex(@"(?-i:[a-z]+)(?i:[k-n]+)");

You can use multiple modifiers in one go like this (?is-m:text), or after another, if you find that clearer (?i)(?s)(?-m)text (I don't). When you use the on/off switching syntax, be aware that the modifier works till the next switch, or the end of the regex. Conversely, using the mode-modified spans, after the span the default behavior will apply.

Finally: the allowed modifiers in .NET are (use a minus to invert the mode):

x allow whitespace and comments
s single-line mode
m multi-line mode
i case insensitivity
n only allow explicit capture (.NET specific)

A bash script `ci` to help you write commit message and commit with OPEN AI/Groq API

Writing commit message could be time-consuming if you want a good summrize. ChatGPT just released their cheapest but still powerful model gpt-4o-mini, we can use it to save our time and keep the commit message accurate.

Attention

I refine the following script time by time and keep the latest version on github. Click here to see the latest version!

You need to put your API KEY in environment variable OPENAI_API_KEY before running this script. You need to run git add first before running this ci script. If you don't want to bother adding first, change git diff --cached -w -b to git diff -w -b and add -a option in the commit command (the last line of the script)

#!/bin/bash

# Get git changes
changes=$(git diff --cached -w -b)

# Exit if no changes
if [ -z "$changes" ]; then
  echo "No changes to commit."
  exit 0
fi

# Set prompt template
prompt_template=$(cat <<-END
You are skilled at writing git commit messages and follow the conventional commits specification:

    refactor: for refactoring
    fix: for bug fixes
    minor: for minor changes
    ...

You will be given the changes from a 'git diff -w -b HEAD' command.
Please summarize all meaningful changes into one commit message, no more than 100 characters.
Respond only with the commit message. Do not mention unchanged aspects. Do not put doube quote in the message. Do not escape underscore characters.
DO REVISE THE MESSAGE THREE TIMES before sending it out. And again: no more than 100 characters!!!
Here are the changes:

END
)

prompt_template+="$changes"

# Build JSON request body using jq
request_body=$(jq -n --arg model "gpt-4o-mini-2024-07-18" --arg prompt "$prompt_template" \
  '{model: $model, messages: [{role:"system",content:"You are a programmer"},{role: "user", content: $prompt}], max_tokens: 32768, seed: 45, temperature: 0}')

# Use curl to request the OpenAI API to generate a commit message
response=$(curl -s --request POST \
  --url https://api.openai.com/v1/chat/completions \
  --header "Authorization: Bearer $OPENAI_API_KEY" \
  --header "Content-Type: application/json" \
  --data "$request_body")

# Extract commit message from the response
commit_message=$(echo "$response" | jq -r '.choices[0].message.content')

# Exit if the commit message is empty
if [ -z "$commit_message" ] || [ "$commit_message" == "null" ]; then
  echo 'failed to generate a commit message:'
  echo $response
  exit 1
fi

# Commit changes
git commit -m "$commit_message"
```bash
**2024-07-24 update**
## Free alternative provided by Groq

If you don't want to pay for the ChatGPT API, you can opt for the free alternative provided by [Groq](https://groq.com). To get started, simply sign up for a Groq account using Google Login. Once you've created your account, you can generate an API key by following this link: <https://console.groq.com/keys>. Finally, make a minor modification to your script, as outlined below.

Build JSON request body using jq

#request_body=$(jq -n arg model "gpt-4o-mini-2024-07-18" arg prompt "$prompt_template" \

'{model: $model, messages: [{role:"system",content:"You are a programmer"},{role: "user", content: $prompt}], max_tokens: 50, seed: 45, temperature: 0}')

#response=$(curl -s --request POST \

--url https://api.openai.com/v1/chat/completions \

--header "Authorization: Bearer $OPENAI_API_KEY" \

--header "Content-Type: application/json" \

--data "$request_body")

request_body=$(jq -n arg model "mixtral-8x7b-32768" arg prompt "$prompt_template"
'{model: $model, messages: [{role:"system",content:"You are a programmer"},{role: "user", content: $prompt}], max_tokens: 32768, stream: false, temperature: 0, top_p: 1, stop: null}')

Use curl to request the OpenAI API to generate a commit message

response=$(curl -s request POST
url https://api.groq.com/openai/v1/chat/completions
header "Authorization: Bearer $GROQ_API_KEY"
header "Content-Type: application/json"
--data "$request_body")

Dart `...` Spread Operator: Simplifying Conditional Widget Addition in Flutter

The Dart ... spread operator is a powerful feature that allows you to insert multiple elements from a collection into another collection. This is particularly useful when building Flutter widget trees, as it can make your code more concise and readable.

What is the Spread Operator ...?

The ... operator is used to expand elements of a collection and add them to another collection. For example:

List<int> list1 = [1, 2, 3];
List<int> list2 = [0, ...list1, 4];
print(list2); // Output: [0, 1, 2, 3, 4]

Using the Spread Operator in Flutter Widgets

In Flutter, you can use the ... operator to conditionally add multiple widgets to a widget list, making the code cleaner and more maintainable.

Without the Spread Operator

If you don't use the ... operator, you might end up writing repetitive conditional checks like this:

AppBar(
  title: const Text('Note Details'),
  actions: [
    if (widget.note.userId == UserSession().id)
      IconButton(
        icon: const Icon(Icons.edit),
        onPressed: _enterEditingMode,
      ),
    if (widget.note.userId == UserSession().id)
      IconButton(
        icon: const Icon(Icons.delete),
        onPressed: () async {
          await DialogService.showConfirmDialog(
            context,
            title: 'Delete note',
            text: 'Each note is a story, are you sure you want to delete it?',
            yesCallback: () => _controller.deleteNote(context, widget.note.id),
          );
        },
      ),
  ],
)

This approach involves duplicating the condition check for each widget, making the code verbose and harder to maintain.

With the Spread Operator

Using the ... spread operator, you can simplify the code by grouping the widgets under a single conditional check:

AppBar(
  title: const Text('Note Details'),
  actions: [
    if (widget.note.userId == UserSession().id) ...[
      IconButton(
        icon: const Icon(Icons.edit),
        onPressed: _enterEditingMode,
      ),
      IconButton(
        icon: const Icon(Icons.delete),
        onPressed: () async {
          await DialogService.showConfirmDialog(
            context,
            title: 'Delete note',
            text: 'Each note is a story, are you sure you want to delete it?',
            yesCallback: () => _controller.deleteNote(context, widget.note.id),
          );
        },
      ),
    ],
  ],
)

This method is more concise and only requires a single condition check, reducing redundancy and improving readability.

Benefits of Using the Spread Operator

  1. Reduces Code Duplication: The spread operator allows you to write less code by removing the need for multiple conditional checks.
  2. Improves Readability and Maintainability: With fewer lines of code and clearer structure, your code is easier to read and maintain.
  3. Simplifies Conditional Addition of Multiple Elements: When you need to add multiple elements based on a condition, the spread operator provides a clean and efficient way to do so.

Using RouteObserver to Refresh a widget when you go back

In a recent project, I ran into an issue where I needed to refresh a page when the user navigated back to it. After searching on google and asking ChatGPT, I found this simple and clean solution.

What is RouteObserver?

RouteObserver is part of the Flutter framework that helps you track navigation events in your app. It allows you to listen to changes in the route stack, such as when a route is pushed or popped. This is incredibly useful for scenarios where you need to refresh or update your UI based on navigation.

Setting Up RouteObserver

First, I created a UserSession class to hold a singleton instance of RouteObserver. Here’s the gist:

// user_session.dart
import 'package:flutter/material.dart';

class UserSession {
  static final UserSession _instance = UserSession._internal();
  static final routeObserver = RouteObserver<ModalRoute>();

  UserSession._internal();
}

By doing this, I could easily access routeObserver from anywhere in my app. My UserSession also holds other user session data, maybe a bettway is to create a separate file for RouteObserver. But for now, we just use the UserSession class.

Adding RouteObserver to Your App

Next, I needed to register this RouteObserver with my app’s navigator. I did this in the main app widget:

// main.dart
import 'package:flutter/material.dart';
import 'package:happy_notes/screens/account/user_session.dart';

class HappyNotesApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: const InitialPage(),
      navigatorObservers: [UserSession.routeObserver],
    );
  }
}

By adding UserSession.routeObserver to navigatorObservers, we’re now tracking route changes across the entire app.

Using RouteObserver in Screens

To use RouteObserver, I extended my state classes with RouteAware and subscribed to the routeObserver in the initState method. Here’s an example from the Memories screen:

// memories.dart
import 'package:flutter/material.dart';
import '../account/user_session.dart';

class MemoriesState extends State<Memories> with RouteAware {
  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addPostFrameCallback((_) {
      UserSession.routeObserver.subscribe(this, ModalRoute.of(context)!);
    });
  }

  @override
  void didPopNext() {
    // Called when the top route has been popped off, and this route shows up
    _fetchMemories();
    setState(() {});
  }

  @override
  void dispose() {
    UserSession.routeObserver.unsubscribe(this);
    super.dispose();
  }
  
  void _fetchMemories() {
    // Logic to refresh memories
  }
}

Here’s what’s happening:

  1. Subscription: In initState, the screen subscribes to routeObserver.
  2. Route Events: The didPopNext method is triggered when the screen becomes visible again after another screen is popped off. This is where I refreshed the screen's data by calling _fetchMemories().
  3. Unsubscription: It’s important to unsubscribe in the dispose method to avoid memory leaks.

Conclusion

Using RouteObserver in Flutter allows you to manage your app’s navigation state effectively. By listening to route changes, you can ensure that your UI stays in sync with user actions, providing a seamless experience.

Best Practices for Handling Time Zones in Daylight Saving Regions

Managing time zones can be tricky, especially in regions that observe daylight saving time (DST). Using the right time zone identifiers can simplify your work by automatically adjusting for these changes. Here’s a quick guide to best practices for handling time zones, using examples from New Zealand and Australia.

The Problem with Fixed Time Zones

The Problem with Fixed Time Zones Using fixed time zones like NZST (New Zealand Standard Time) typically doesn't account for daylight saving changes, potentially leading to incorrect time calculations during daylight saving periods. However, certain platforms, such as .NET, which handles these transitions internally, giving the correct local time. This behavior might not be consistent across different platforms and systems.

The Best Practice Solution

The best practice is to use time zone identifiers that handle both standard and daylight saving times automatically. These identifiers switch between standard time and daylight saving time based on the current date, keeping your times accurate year-round. For example:

  • New Zealand: Use Pacific/Auckland.
  • Australia: Use Australia/Sydney.
  • United States: Use America/New_York for Eastern Time, America/Los_Angeles for Pacific Time, etc.

These identifiers know when to transition between standard time and daylight saving time, ensuring your applications reflect the correct local time.