mirror of
https://github.com/DefinedNet/mobile_nebula.git
synced 2025-09-07 19:46:06 +00:00
Compare commits
6 commits
a9829d80c6
...
418d030d1f
Author | SHA1 | Date | |
---|---|---|---|
|
418d030d1f | ||
|
fd991b9a02 | ||
|
768266b15f | ||
|
2ee91ace02 | ||
|
f7d22824f8 | ||
|
f3d3b8885e |
8 changed files with 109 additions and 130 deletions
29
README.md
29
README.md
|
@ -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>.
|
|
@ -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',
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -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',
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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)) {
|
||||
|
|
Loading…
Add table
Reference in a new issue