Compare commits

...

6 commits

Author SHA1 Message Date
Caleb Jasik
418d030d1f
Merge 768266b15f into fd991b9a02 2025-03-05 15:00:46 +00:00
Caleb Jasik
fd991b9a02
Fix some flutter analyze warnings (#268)
* Remove unneccesary Container instances

* Add types to uninitialized variables

* Use the parameter initializing shorthand instead of doing it in the constructor
2025-03-05 08:00:44 -06:00
Caleb Jasik
768266b15f
Update casing and grammar from feedback 2025-02-26 12:30:02 -06:00
Caleb Jasik
2ee91ace02
Write out the entire release process in steps 2025-02-26 12:30:02 -06:00
Caleb Jasik
f7d22824f8
Update how releasing works 2025-02-26 12:30:02 -06:00
Caleb Jasik
f3d3b8885e
Update README with release instructions and spelling changes 2025-02-26 12:29:59 -06:00
8 changed files with 109 additions and 130 deletions

View file

@ -22,11 +22,11 @@ Run `flutter doctor` and fix everything it complains before proceeding
- Copy `env.sh.example` and set it up for your machine
- Ensure you have run `gomobile init`
- In Android Studio, make sure you have the current ndk installed by going to Tools -> SDK Manager, go to the SDK Tools tab, check the `Show package details` box, expand the NDK section and select `27.0.12077973` version.
- Ensure you have downloaded an ndk via android studio, this is likely not the default one and you need to check the
- In Android Studio, make sure you have the current NDK installed by going to Tools → SDK Manager, go to the SDK Tools tab, check the `Show package details` box, expand the NDK section and select `27.0.12077973` version.
- Ensure you have downloaded an NDK via android studio, this is likely not the default one, and you need to check the
`Show package details` box to select the correct version. The correct version comes from the error when you try and compile
- Make sure you have `gem` installed with `sudo gem install`
- If on MacOS arm, `sudo gem install ffi -- --enable-libffi-alloc`
- If on macOS arm64, `sudo gem install ffi -- --enable-libffi-alloc`
If you are having issues with iOS pods, try blowing it all away! `cd ios && rm -rf Pods/ Podfile.lock && pod install --repo-update`
@ -39,7 +39,11 @@ Use:
dart format lib/ test/ -l 120
```
In Android Studio, set the line length using Preferences -> Editor -> Code Style -> Dart -> Line length, set it to 120. Enable auto-format with Preferences -> Languages & Frameworks -> Flutter -> Format code on save.
In Android Studio, set the line length using Preferences → Editor → Code Style → Dart → Line length, set it to 120. Enable auto-format with Preferences → Languages & Frameworks → Flutter → Format code on save.
# Prerelease
Push a git tag `v#.#.#-##`, e.g. `v0.5.1-0`, and `.github/release.yml` will build a draft release and publish it to iOS TestFlight and Android internal track.
`./swift-format.sh` can be used to format Swift code in the repo.
@ -47,16 +51,7 @@ Once `swift-format` supports ignoring directories (<https://github.com/swiftlang
# Release
Update `version` in `pubspec.yaml` to reflect this release, then
## Android
`flutter build appbundle`
This will create an android app bundle at `build/app/outputs/bundle/release/`
Upload the android bundle to the google play store https://play.google.com/apps/publish
## iOS
In xcode, Release -> Archive then follow the directions to upload to the app store. If you have issues, https://flutter.dev/docs/deployment/ios#create-a-build-archive
1. Manually promote a prerelease build from TestFlight and Android internal track to the corresponding public app stores.
2. Mark the associated draft release as published, removing the `-##` from it, ending with a release in the format `v#.#.#`, e.g. `v0.5.1`.
3. Remove the old draft releases that will never be published.
4. Add the notable changes to the app to the release summary, e.g.: <https://github.com/DefinedNet/mobile_nebula/releases/tag/v0.5.1>.

View file

@ -2,6 +2,7 @@ import 'package:flutter/cupertino.dart';
import 'package:flutter/services.dart';
import 'package:mobile_nebula/components/SpecialTextField.dart';
import 'package:mobile_nebula/models/CIDR.dart';
import '../services/utils.dart';
import 'IPField.dart';
@ -52,57 +53,55 @@ class _CIDRFieldState extends State<CIDRField> {
Widget build(BuildContext context) {
var textStyle = CupertinoTheme.of(context).textTheme.textStyle;
return Container(
child: Row(
children: <Widget>[
Expanded(
child: Padding(
padding: EdgeInsets.fromLTRB(6, 6, 2, 6),
child: IPField(
help: widget.ipHelp,
ipOnly: true,
textPadding: EdgeInsets.all(0),
textInputAction: TextInputAction.next,
textAlign: TextAlign.end,
focusNode: widget.focusNode,
nextFocusNode: bitsFocus,
onChanged: (val) {
if (widget.onChanged == null) {
return;
}
cidr.ip = val;
widget.onChanged!(cidr);
},
controller: widget.ipController,
),
),
),
Text("/"),
Container(
width: Utils.textSize("bits", textStyle).width + 12,
padding: EdgeInsets.fromLTRB(2, 6, 6, 6),
child: SpecialTextField(
keyboardType: TextInputType.number,
focusNode: bitsFocus,
nextFocusNode: widget.nextFocusNode,
controller: widget.bitsController,
return Row(
children: <Widget>[
Expanded(
child: Padding(
padding: EdgeInsets.fromLTRB(6, 6, 2, 6),
child: IPField(
help: widget.ipHelp,
ipOnly: true,
textPadding: EdgeInsets.all(0),
textInputAction: TextInputAction.next,
textAlign: TextAlign.end,
focusNode: widget.focusNode,
nextFocusNode: bitsFocus,
onChanged: (val) {
if (widget.onChanged == null) {
return;
}
cidr.bits = int.tryParse(val) ?? 0;
cidr.ip = val;
widget.onChanged!(cidr);
},
maxLength: 2,
inputFormatters: [FilteringTextInputFormatter.digitsOnly],
textInputAction: widget.textInputAction ?? TextInputAction.done,
placeholder: 'bits',
controller: widget.ipController,
),
),
],
),
),
Text("/"),
Container(
width: Utils.textSize("bits", textStyle).width + 12,
padding: EdgeInsets.fromLTRB(2, 6, 6, 6),
child: SpecialTextField(
keyboardType: TextInputType.number,
focusNode: bitsFocus,
nextFocusNode: widget.nextFocusNode,
controller: widget.bitsController,
onChanged: (val) {
if (widget.onChanged == null) {
return;
}
cidr.bits = int.tryParse(val) ?? 0;
widget.onChanged!(cidr);
},
maxLength: 2,
inputFormatters: [FilteringTextInputFormatter.digitsOnly],
textInputAction: widget.textInputAction ?? TextInputAction.done,
placeholder: 'bits',
),
),
],
);
}

View file

@ -2,6 +2,7 @@ import 'package:flutter/cupertino.dart';
import 'package:flutter/services.dart';
import 'package:mobile_nebula/components/SpecialTextField.dart';
import 'package:mobile_nebula/models/IPAndPort.dart';
import '../services/utils.dart';
import 'IPField.dart';
@ -58,49 +59,47 @@ class _IPAndPortFieldState extends State<IPAndPortField> {
Widget build(BuildContext context) {
var textStyle = CupertinoTheme.of(context).textTheme.textStyle;
return Container(
child: Row(
children: <Widget>[
Expanded(
child: Padding(
padding: EdgeInsets.fromLTRB(6, 6, 2, 6),
child: IPField(
help: widget.ipHelp,
ipOnly: widget.ipOnly,
nextFocusNode: _portFocus,
textPadding: EdgeInsets.all(0),
textInputAction: TextInputAction.next,
focusNode: widget.focusNode,
onChanged: (val) {
_ipAndPort.ip = val;
widget.onChanged(_ipAndPort);
},
textAlign: widget.ipTextAlign,
controller: widget.ipController,
),
),
),
Text(":"),
Container(
width: Utils.textSize("00000", textStyle).width + 12,
padding: EdgeInsets.fromLTRB(2, 6, 6, 6),
child: SpecialTextField(
keyboardType: TextInputType.number,
focusNode: _portFocus,
nextFocusNode: widget.nextFocusNode,
controller: widget.portController,
return Row(
children: <Widget>[
Expanded(
child: Padding(
padding: EdgeInsets.fromLTRB(6, 6, 2, 6),
child: IPField(
help: widget.ipHelp,
ipOnly: widget.ipOnly,
nextFocusNode: _portFocus,
textPadding: EdgeInsets.all(0),
textInputAction: TextInputAction.next,
focusNode: widget.focusNode,
onChanged: (val) {
_ipAndPort.port = int.tryParse(val);
_ipAndPort.ip = val;
widget.onChanged(_ipAndPort);
},
maxLength: 5,
inputFormatters: [FilteringTextInputFormatter.digitsOnly],
textInputAction: TextInputAction.done,
placeholder: 'port',
textAlign: widget.ipTextAlign,
controller: widget.ipController,
),
),
],
),
),
Text(":"),
Container(
width: Utils.textSize("00000", textStyle).width + 12,
padding: EdgeInsets.fromLTRB(2, 6, 6, 6),
child: SpecialTextField(
keyboardType: TextInputType.number,
focusNode: _portFocus,
nextFocusNode: widget.nextFocusNode,
controller: widget.portController,
onChanged: (val) {
_ipAndPort.port = int.tryParse(val);
widget.onChanged(_ipAndPort);
},
maxLength: 5,
inputFormatters: [FilteringTextInputFormatter.digitsOnly],
textInputAction: TextInputAction.done,
placeholder: 'port',
),
),
],
);
}

View file

@ -9,7 +9,7 @@ class SiteItem extends StatelessWidget {
const SiteItem({super.key, required this.site, this.onPressed});
final Site site;
final onPressed;
final void Function()? onPressed;
@override
Widget build(BuildContext context) {

View file

@ -8,7 +8,7 @@ class ConfigButtonItem extends StatelessWidget {
const ConfigButtonItem({super.key, this.content, this.onPressed});
final Widget? content;
final onPressed;
final void Function()? onPressed;
@override
Widget build(BuildContext context) {

View file

@ -2,7 +2,6 @@ import 'dart:io';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:mobile_nebula/components/SpecialButton.dart';
import 'package:mobile_nebula/services/utils.dart';
@ -21,7 +20,7 @@ class ConfigPageItem extends StatelessWidget {
final Widget? content;
final double labelWidth;
final CrossAxisAlignment crossAxisAlignment;
final onPressed;
final void Function()? onPressed;
final bool disabled;
@override

View file

@ -5,6 +5,7 @@ import 'package:flutter/services.dart';
import 'package:mobile_nebula/models/HostInfo.dart';
import 'package:mobile_nebula/models/UnsafeRoute.dart';
import 'package:uuid/uuid.dart';
import 'Certificate.dart';
import 'StaticHosts.dart';
@ -53,45 +54,31 @@ class Site {
late List<String> errors;
Site({
String name = '',
this.name = '',
String? id,
Map<String, StaticHost>? staticHostmap,
List<CertificateInfo>? ca,
CertificateInfo? certInfo,
int lhDuration = 0,
int port = 0,
String cipher = "aes",
int sortKey = 0,
int mtu = 1300,
bool connected = false,
String status = '',
String logFile = '',
String logVerbosity = 'info',
this.certInfo,
this.lhDuration = 0,
this.port = 0,
this.cipher = "aes",
this.sortKey = 0,
this.mtu = 1300,
this.connected = false,
this.status = '',
this.logFile = '',
this.logVerbosity = 'info',
List<String>? errors,
List<UnsafeRoute>? unsafeRoutes,
bool managed = false,
String? rawConfig,
DateTime? lastManagedUpdate,
this.managed = false,
this.rawConfig,
this.lastManagedUpdate,
}) {
this.name = name;
this.id = id ?? uuid.v4();
this.staticHostmap = staticHostmap ?? {};
this.ca = ca ?? [];
this.certInfo = certInfo;
this.lhDuration = lhDuration;
this.port = port;
this.cipher = cipher;
this.sortKey = sortKey;
this.mtu = mtu;
this.connected = connected;
this.status = status;
this.logFile = logFile;
this.logVerbosity = logVerbosity;
this.errors = errors ?? [];
this.unsafeRoutes = unsafeRoutes ?? [];
this.managed = managed;
this.rawConfig = rawConfig;
this.lastManagedUpdate = lastManagedUpdate;
_updates = EventChannel('net.defined.nebula/${this.id}');
_updates.receiveBroadcastStream().listen(

View file

@ -5,7 +5,7 @@ bool dnsValidator(str, {requireTld = true, allowUnderscore = false}) {
return false;
}
List parts = str.split('.');
List<String> parts = str.split('.');
if (requireTld) {
var tld = parts.removeLast();
if (parts.isEmpty || !RegExp(r'^[a-z]{2,}$').hasMatch(tld)) {