Compare commits
4 Commits
v0.6.2-dev
...
v0.6.3-dev
| Author | SHA1 | Date | |
|---|---|---|---|
| af37e25692 | |||
|
|
1689869ce5 | ||
|
|
425ab46656 | ||
| 196511dfab |
@@ -3,7 +3,7 @@ name: Release
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- dev
|
- dev-other
|
||||||
|
|
||||||
env:
|
env:
|
||||||
JAVA_VERSION: "17"
|
JAVA_VERSION: "17"
|
||||||
@@ -85,16 +85,16 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
#
|
#
|
||||||
# - name: Install OS deps (Android)
|
# - name: Install OS deps (Android)
|
||||||
# run: |
|
# run: |
|
||||||
# if command -v sudo >/dev/null 2>&1; then
|
# if command -v sudo >/dev/null 2>&1; then
|
||||||
# SUDO="sudo"
|
# SUDO="sudo"
|
||||||
# else
|
# else
|
||||||
# SUDO=""
|
# SUDO=""
|
||||||
# fi
|
# fi
|
||||||
# $SUDO apt-get update
|
# $SUDO apt-get update
|
||||||
# $SUDO apt-get install -y unzip xz-utils zip libstdc++6 liblzma-dev curl jq
|
# $SUDO apt-get install -y unzip xz-utils zip libstdc++6 liblzma-dev curl jq
|
||||||
|
|
||||||
- name: Setup Java
|
- name: Setup Java
|
||||||
uses: actions/setup-java@v4
|
uses: actions/setup-java@v4
|
||||||
@@ -120,19 +120,19 @@ jobs:
|
|||||||
echo "$ANDROID_SDK_ROOT/platform-tools" >> "$GITHUB_PATH"
|
echo "$ANDROID_SDK_ROOT/platform-tools" >> "$GITHUB_PATH"
|
||||||
echo "$ANDROID_SDK_ROOT/build-tools/33.0.2" >> "$GITHUB_PATH"
|
echo "$ANDROID_SDK_ROOT/build-tools/33.0.2" >> "$GITHUB_PATH"
|
||||||
|
|
||||||
# - name: Install Flutter SDK
|
# - name: Install Flutter SDK
|
||||||
# run: |
|
# run: |
|
||||||
# set -euo pipefail
|
# set -euo pipefail
|
||||||
# FLUTTER_HOME="$HOME/flutter"
|
# FLUTTER_HOME="$HOME/flutter"
|
||||||
# # Avoid git ownership issues when Flutter checks out deps.
|
# # Avoid git ownership issues when Flutter checks out deps.
|
||||||
# git config --global --add safe.directory "$FLUTTER_HOME" || true
|
# git config --global --add safe.directory "$FLUTTER_HOME" || true
|
||||||
# if [ ! -x "$FLUTTER_HOME/bin/flutter" ]; then
|
# if [ ! -x "$FLUTTER_HOME/bin/flutter" ]; then
|
||||||
# rm -rf "$FLUTTER_HOME"
|
# rm -rf "$FLUTTER_HOME"
|
||||||
# curl -fsSL -o /tmp/flutter.tar.xz "https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/flutter_linux_${FLUTTER_VERSION}-stable.tar.xz"
|
# curl -fsSL -o /tmp/flutter.tar.xz "https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/flutter_linux_${FLUTTER_VERSION}-stable.tar.xz"
|
||||||
# tar -C "$HOME" -xf /tmp/flutter.tar.xz
|
# tar -C "$HOME" -xf /tmp/flutter.tar.xz
|
||||||
# fi
|
# fi
|
||||||
# echo "$FLUTTER_HOME/bin" >> "$GITHUB_PATH"
|
# echo "$FLUTTER_HOME/bin" >> "$GITHUB_PATH"
|
||||||
# "$FLUTTER_HOME/bin/flutter" --version
|
# "$FLUTTER_HOME/bin/flutter" --version
|
||||||
|
|
||||||
- name: Allow all git directories (CI)
|
- name: Allow all git directories (CI)
|
||||||
run: git config --global --add safe.directory '*'
|
run: git config --global --add safe.directory '*'
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
|
- dev
|
||||||
|
|
||||||
env:
|
env:
|
||||||
JAVA_VERSION: "17"
|
JAVA_VERSION: "17"
|
||||||
@@ -12,6 +13,7 @@ env:
|
|||||||
BUILD_WINDOWS: "false" # Windows build disabled (no runner available)
|
BUILD_WINDOWS: "false" # Windows build disabled (no runner available)
|
||||||
GITEA_BASE_URL: https://git.tgj.services
|
GITEA_BASE_URL: https://git.tgj.services
|
||||||
WEB_IMAGE: "git.tgj.services/petegregoryy/mileograph-web"
|
WEB_IMAGE: "git.tgj.services/petegregoryy/mileograph-web"
|
||||||
|
REGISTRY: git.tgj.services
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
meta:
|
meta:
|
||||||
@@ -79,21 +81,21 @@ jobs:
|
|||||||
|
|
||||||
android-build:
|
android-build:
|
||||||
runs-on:
|
runs-on:
|
||||||
- mileograph
|
- tgj-arc
|
||||||
needs: meta
|
needs: meta
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
#
|
||||||
- name: Install OS deps (Android)
|
# - name: Install OS deps (Android)
|
||||||
run: |
|
# run: |
|
||||||
if command -v sudo >/dev/null 2>&1; then
|
# if command -v sudo >/dev/null 2>&1; then
|
||||||
SUDO="sudo"
|
# SUDO="sudo"
|
||||||
else
|
# else
|
||||||
SUDO=""
|
# SUDO=""
|
||||||
fi
|
# fi
|
||||||
$SUDO apt-get update
|
# $SUDO apt-get update
|
||||||
$SUDO apt-get install -y unzip xz-utils zip libstdc++6 liblzma-dev curl jq
|
# $SUDO apt-get install -y unzip xz-utils zip libstdc++6 liblzma-dev curl jq
|
||||||
|
|
||||||
- name: Setup Java
|
- name: Setup Java
|
||||||
uses: actions/setup-java@v4
|
uses: actions/setup-java@v4
|
||||||
@@ -119,19 +121,19 @@ jobs:
|
|||||||
echo "$ANDROID_SDK_ROOT/platform-tools" >> "$GITHUB_PATH"
|
echo "$ANDROID_SDK_ROOT/platform-tools" >> "$GITHUB_PATH"
|
||||||
echo "$ANDROID_SDK_ROOT/build-tools/33.0.2" >> "$GITHUB_PATH"
|
echo "$ANDROID_SDK_ROOT/build-tools/33.0.2" >> "$GITHUB_PATH"
|
||||||
|
|
||||||
- name: Install Flutter SDK
|
# - name: Install Flutter SDK
|
||||||
run: |
|
# run: |
|
||||||
set -euo pipefail
|
# set -euo pipefail
|
||||||
FLUTTER_HOME="$HOME/flutter"
|
# FLUTTER_HOME="$HOME/flutter"
|
||||||
# Avoid git ownership issues when Flutter checks out deps.
|
# # Avoid git ownership issues when Flutter checks out deps.
|
||||||
git config --global --add safe.directory "$FLUTTER_HOME" || true
|
# git config --global --add safe.directory "$FLUTTER_HOME" || true
|
||||||
if [ ! -x "$FLUTTER_HOME/bin/flutter" ]; then
|
# if [ ! -x "$FLUTTER_HOME/bin/flutter" ]; then
|
||||||
rm -rf "$FLUTTER_HOME"
|
# rm -rf "$FLUTTER_HOME"
|
||||||
curl -fsSL -o /tmp/flutter.tar.xz "https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/flutter_linux_${FLUTTER_VERSION}-stable.tar.xz"
|
# curl -fsSL -o /tmp/flutter.tar.xz "https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/flutter_linux_${FLUTTER_VERSION}-stable.tar.xz"
|
||||||
tar -C "$HOME" -xf /tmp/flutter.tar.xz
|
# tar -C "$HOME" -xf /tmp/flutter.tar.xz
|
||||||
fi
|
# fi
|
||||||
echo "$FLUTTER_HOME/bin" >> "$GITHUB_PATH"
|
# echo "$FLUTTER_HOME/bin" >> "$GITHUB_PATH"
|
||||||
"$FLUTTER_HOME/bin/flutter" --version
|
# "$FLUTTER_HOME/bin/flutter" --version
|
||||||
|
|
||||||
- name: Allow all git directories (CI)
|
- name: Allow all git directories (CI)
|
||||||
run: git config --global --add safe.directory '*'
|
run: git config --global --add safe.directory '*'
|
||||||
@@ -220,36 +222,12 @@ jobs:
|
|||||||
|
|
||||||
linux-build:
|
linux-build:
|
||||||
runs-on:
|
runs-on:
|
||||||
- mileograph
|
- tgj-arc
|
||||||
needs: meta
|
needs: meta
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Install OS deps (Linux desktop)
|
|
||||||
run: |
|
|
||||||
if command -v sudo >/dev/null 2>&1; then
|
|
||||||
SUDO="sudo"
|
|
||||||
else
|
|
||||||
SUDO=""
|
|
||||||
fi
|
|
||||||
$SUDO apt-get update
|
|
||||||
$SUDO apt-get install -y unzip xz-utils zip libstdc++6 libglu1-mesa clang cmake ninja-build pkg-config libgtk-3-dev libsecret-1-dev liblzma-dev curl jq
|
|
||||||
|
|
||||||
- name: Install Flutter SDK
|
|
||||||
run: |
|
|
||||||
set -euo pipefail
|
|
||||||
FLUTTER_HOME="$HOME/flutter"
|
|
||||||
# Avoid git ownership issues when Flutter checks out deps.
|
|
||||||
git config --global --add safe.directory "$FLUTTER_HOME" || true
|
|
||||||
if [ ! -x "$FLUTTER_HOME/bin/flutter" ]; then
|
|
||||||
rm -rf "$FLUTTER_HOME"
|
|
||||||
curl -fsSL -o /tmp/flutter.tar.xz "https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/flutter_linux_${FLUTTER_VERSION}-stable.tar.xz"
|
|
||||||
tar -C "$HOME" -xf /tmp/flutter.tar.xz
|
|
||||||
fi
|
|
||||||
echo "$FLUTTER_HOME/bin" >> "$GITHUB_PATH"
|
|
||||||
"$FLUTTER_HOME/bin/flutter" --version
|
|
||||||
|
|
||||||
- name: Allow all git directories (CI)
|
- name: Allow all git directories (CI)
|
||||||
run: git config --global --add safe.directory '*'
|
run: git config --global --add safe.directory '*'
|
||||||
|
|
||||||
@@ -275,38 +253,12 @@ jobs:
|
|||||||
|
|
||||||
web-build:
|
web-build:
|
||||||
runs-on:
|
runs-on:
|
||||||
- mileograph
|
- tgj-arc
|
||||||
needs: meta
|
needs: meta
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Install OS deps (Web)
|
|
||||||
run: |
|
|
||||||
if command -v sudo >/dev/null 2>&1; then
|
|
||||||
SUDO="sudo"
|
|
||||||
else
|
|
||||||
SUDO=""
|
|
||||||
fi
|
|
||||||
$SUDO apt-get update
|
|
||||||
$SUDO apt-get install -y unzip xz-utils zip libstdc++6 liblzma-dev curl jq docker.io
|
|
||||||
if ! docker info >/dev/null 2>&1; then
|
|
||||||
$SUDO systemctl start docker 2>/dev/null || $SUDO service docker start 2>/dev/null || true
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Install Flutter SDK
|
|
||||||
run: |
|
|
||||||
set -euo pipefail
|
|
||||||
FLUTTER_HOME="$HOME/flutter"
|
|
||||||
git config --global --add safe.directory "$FLUTTER_HOME" || true
|
|
||||||
if [ ! -x "$FLUTTER_HOME/bin/flutter" ]; then
|
|
||||||
rm -rf "$FLUTTER_HOME"
|
|
||||||
curl -fsSL -o /tmp/flutter.tar.xz "https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/flutter_linux_${FLUTTER_VERSION}-stable.tar.xz"
|
|
||||||
tar -C "$HOME" -xf /tmp/flutter.tar.xz
|
|
||||||
fi
|
|
||||||
echo "$FLUTTER_HOME/bin" >> "$GITHUB_PATH"
|
|
||||||
"$FLUTTER_HOME/bin/flutter" --version
|
|
||||||
|
|
||||||
- name: Allow all git directories (CI)
|
- name: Allow all git directories (CI)
|
||||||
run: git config --global --add safe.directory '*'
|
run: git config --global --add safe.directory '*'
|
||||||
|
|
||||||
@@ -330,50 +282,38 @@ jobs:
|
|||||||
name: web-build
|
name: web-build
|
||||||
path: app-web.tar.gz
|
path: app-web.tar.gz
|
||||||
|
|
||||||
- name: Compute web image tags
|
- name: Set up docker buildx
|
||||||
id: web_meta
|
uses: docker/setup-buildx-action@v3
|
||||||
env:
|
|
||||||
BASE_VERSION: ${{ needs.meta.outputs.base_version }}
|
|
||||||
DEV_SUFFIX: ${{ needs.meta.outputs.dev_suffix }}
|
|
||||||
run: |
|
|
||||||
IMAGE="${WEB_IMAGE}"
|
|
||||||
TAG=""
|
|
||||||
ALIAS=""
|
|
||||||
if [ "${GITHUB_REF}" = "refs/heads/dev" ]; then
|
|
||||||
TAG="${BASE_VERSION}${DEV_SUFFIX}"
|
|
||||||
ALIAS="dev"
|
|
||||||
elif [ "${GITHUB_REF}" = "refs/heads/master" ]; then
|
|
||||||
TAG="${BASE_VERSION}"
|
|
||||||
ALIAS="latest"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "image=${IMAGE}" >> "$GITHUB_OUTPUT"
|
- name: Docker meta
|
||||||
echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
|
id: dmeta
|
||||||
echo "alias=${ALIAS}" >> "$GITHUB_OUTPUT"
|
uses: docker/metadata-action@v5
|
||||||
|
with:
|
||||||
|
images: ${{ env.REGISTRY }}/petegregoryy/railframe-web
|
||||||
|
flavor: latest=false
|
||||||
|
tags: |
|
||||||
|
type=sha,prefix=
|
||||||
|
type=raw,value=${{ needs.meta.outputs.base_version }}${{ needs.meta.outputs.dev_suffix }}
|
||||||
|
type=raw,value=dev
|
||||||
|
|
||||||
- name: Login to registry
|
- name: Login to the docker registry
|
||||||
if: ${{ secrets.DOCKERHUB_TOKEN != '' && steps.web_meta.outputs.tag != '' }}
|
uses: docker/login-action@v3
|
||||||
env:
|
with:
|
||||||
REGISTRY_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
|
registry: ${{ env.REGISTRY }}
|
||||||
run: |
|
username: petegregoryy
|
||||||
echo "$REGISTRY_TOKEN" | docker login git.tgj.services -u petegregoryy --password-stdin
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Build and push web image
|
- name: Build and push
|
||||||
if: ${{ secrets.DOCKERHUB_TOKEN != '' && steps.web_meta.outputs.tag != '' }}
|
id: docker_build
|
||||||
env:
|
uses: docker/build-push-action@v6
|
||||||
IMAGE: ${{ steps.web_meta.outputs.image }}
|
with:
|
||||||
TAG: ${{ steps.web_meta.outputs.tag }}
|
file: Dockerfile.web
|
||||||
ALIAS: ${{ steps.web_meta.outputs.alias }}
|
context: .
|
||||||
run: |
|
push: true
|
||||||
docker buildx create --name buildx --driver=docker-container --use || docker buildx use buildx
|
tags: ${{ steps.dmeta.outputs.tags }}
|
||||||
TAG_ARGS=(-t "${IMAGE}:${TAG}")
|
labels: ${{ steps.dmeta.outputs.labels }}
|
||||||
if [ -n "$ALIAS" ]; then
|
cache-from: type=registry,ref=${{ env.REGISTRY }}/petegregoryy/railframe-web:buildcache
|
||||||
TAG_ARGS+=(-t "${IMAGE}:${ALIAS}")
|
cache-to: type=registry,ref=${{ env.REGISTRY }}/petegregoryy/railframe-web:buildcache,mode=max
|
||||||
fi
|
|
||||||
docker buildx build --builder buildx --platform linux/amd64 \
|
|
||||||
-f Dockerfile.web \
|
|
||||||
--push \
|
|
||||||
"${TAG_ARGS[@]}" .
|
|
||||||
|
|
||||||
release-dev:
|
release-dev:
|
||||||
runs-on:
|
runs-on:
|
||||||
|
|||||||
@@ -234,34 +234,62 @@ class _LegCardState extends State<LegCard> {
|
|||||||
padding: const EdgeInsets.fromLTRB(16, 0, 16, 12),
|
padding: const EdgeInsets.fromLTRB(16, 0, 16, 12),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
if (leg.notes.isNotEmpty) ...[
|
if (leg.notes.isNotEmpty) ...[
|
||||||
Text('Notes', style: textTheme.titleSmall),
|
Text('Notes', style: textTheme.titleSmall),
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
Text(leg.notes),
|
Text(leg.notes),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
],
|
],
|
||||||
if (leg.locos.isNotEmpty) ...[
|
if (leg.locos.isNotEmpty) ...[
|
||||||
Text('Locos', style: textTheme.titleSmall),
|
Text('Locos', style: textTheme.titleSmall),
|
||||||
const SizedBox(height: 6),
|
const SizedBox(height: 6),
|
||||||
Wrap(
|
Wrap(
|
||||||
spacing: 8,
|
spacing: 8,
|
||||||
runSpacing: 8,
|
runSpacing: 8,
|
||||||
children: _buildLocoChips(context, leg),
|
children: _buildLocoChips(context, leg),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
],
|
],
|
||||||
if (_hasTrainDetails(leg)) ...[
|
if (_hasTrainDetails(leg)) ...[
|
||||||
Text('Train', style: textTheme.titleSmall),
|
Text('Train', style: textTheme.titleSmall),
|
||||||
const SizedBox(height: 6),
|
const SizedBox(height: 6),
|
||||||
..._buildTrainDetails(leg, textTheme),
|
..._buildTrainDetails(leg, textTheme),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
],
|
],
|
||||||
if (routeSegments.isNotEmpty) ...[
|
if (sharedFrom != null)
|
||||||
Text('Route', style: textTheme.titleSmall),
|
Padding(
|
||||||
const SizedBox(height: 6),
|
padding: const EdgeInsets.only(bottom: 8.0),
|
||||||
_buildRouteList(routeSegments),
|
child: Text(
|
||||||
],
|
'Shared from ${sharedFrom.sharedFromDisplay.isNotEmpty ? sharedFrom.sharedFromDisplay : 'another user'}.',
|
||||||
|
style: textTheme.bodyMedium,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (sharedTo.isNotEmpty) ...[
|
||||||
|
Text(
|
||||||
|
'Shared to:',
|
||||||
|
style: textTheme.bodyMedium,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
Wrap(
|
||||||
|
spacing: 8,
|
||||||
|
runSpacing: 4,
|
||||||
|
children: sharedTo
|
||||||
|
.map((s) => Chip(
|
||||||
|
label: Text(s.sharedToDisplay.isNotEmpty
|
||||||
|
? s.sharedToDisplay
|
||||||
|
: s.sharedToUserId),
|
||||||
|
visualDensity: VisualDensity.compact,
|
||||||
|
))
|
||||||
|
.toList(),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
],
|
||||||
|
if (routeSegments.isNotEmpty) ...[
|
||||||
|
Text('Route', style: textTheme.titleSmall),
|
||||||
|
const SizedBox(height: 6),
|
||||||
|
_buildRouteList(routeSegments),
|
||||||
|
],
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -324,6 +324,14 @@ extension _NewEntryDraftLogic on _NewEntryPageState {
|
|||||||
"distance": _routeResult!.distance,
|
"distance": _routeResult!.distance,
|
||||||
},
|
},
|
||||||
"tractionItems": _serializeTractionItems(),
|
"tractionItems": _serializeTractionItems(),
|
||||||
|
"shareUserIds": _shareUserIds.toList(),
|
||||||
|
"shareUsers": _shareUsers
|
||||||
|
.map((u) => {
|
||||||
|
"user_id": u.userId,
|
||||||
|
"username": u.username,
|
||||||
|
"full_name": u.fullName,
|
||||||
|
})
|
||||||
|
.toList(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -707,7 +707,7 @@ class _NewEntryPageState extends State<NewEntryPage> {
|
|||||||
final enabled = value ?? false;
|
final enabled = value ?? false;
|
||||||
setState(() {
|
setState(() {
|
||||||
_matchDestinationToEntry = enabled;
|
_matchDestinationToEntry = enabled;
|
||||||
if (enabled) _hasDestinationTime = true;
|
if (enabled && _hasEndTime) _hasDestinationTime = true;
|
||||||
});
|
});
|
||||||
_scheduleMatchUpdate();
|
_scheduleMatchUpdate();
|
||||||
_saveDraft();
|
_saveDraft();
|
||||||
@@ -1127,7 +1127,7 @@ class _NewEntryPageState extends State<NewEntryPage> {
|
|||||||
if (_destinationController.text != endVal) {
|
if (_destinationController.text != endVal) {
|
||||||
_destinationController.text = endVal;
|
_destinationController.text = endVal;
|
||||||
}
|
}
|
||||||
if (_hasDestinationTime) {
|
if (_hasDestinationTime && _hasEndTime) {
|
||||||
final endTime = _legEndDateTime ?? _legDateTime;
|
final endTime = _legEndDateTime ?? _legDateTime;
|
||||||
_selectedDestinationDate = DateTime(
|
_selectedDestinationDate = DateTime(
|
||||||
endTime.year,
|
endTime.year,
|
||||||
@@ -1283,9 +1283,9 @@ class _NewEntryPageState extends State<NewEntryPage> {
|
|||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
_buildSharedBanner(),
|
_buildSharedBanner(),
|
||||||
],
|
],
|
||||||
_buildTripSelector(context),
|
_buildTripSelector(context),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 8),
|
||||||
if (_activeLegShare == null) _buildShareSection(context),
|
if (_activeLegShare == null) _buildShareSection(context),
|
||||||
_dateTimeGroup(
|
_dateTimeGroup(
|
||||||
context,
|
context,
|
||||||
title: 'Departure time',
|
title: 'Departure time',
|
||||||
@@ -1366,6 +1366,7 @@ class _NewEntryPageState extends State<NewEntryPage> {
|
|||||||
border: OutlineInputBorder(),
|
border: OutlineInputBorder(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
const Divider(height: 24),
|
||||||
_trainLocationBlock(
|
_trainLocationBlock(
|
||||||
label: 'Origin',
|
label: 'Origin',
|
||||||
controller: _originController,
|
controller: _originController,
|
||||||
@@ -1384,6 +1385,7 @@ class _NewEntryPageState extends State<NewEntryPage> {
|
|||||||
singleColumn: true,
|
singleColumn: true,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
const Divider(height: 24),
|
||||||
_trainLocationBlock(
|
_trainLocationBlock(
|
||||||
label: 'Destination',
|
label: 'Destination',
|
||||||
controller: _destinationController,
|
controller: _destinationController,
|
||||||
@@ -1403,6 +1405,7 @@ class _NewEntryPageState extends State<NewEntryPage> {
|
|||||||
singleColumn: true,
|
singleColumn: true,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
const Divider(height: 24),
|
||||||
TextFormField(
|
TextFormField(
|
||||||
controller: _networkController,
|
controller: _networkController,
|
||||||
textCapitalization: TextCapitalization.characters,
|
textCapitalization: TextCapitalization.characters,
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import 'package:mileograph_flutter/components/traction/traction_card.dart';
|
|||||||
import 'package:mileograph_flutter/objects/objects.dart';
|
import 'package:mileograph_flutter/objects/objects.dart';
|
||||||
import 'package:mileograph_flutter/services/data_service.dart';
|
import 'package:mileograph_flutter/services/data_service.dart';
|
||||||
import 'package:mileograph_flutter/services/distance_unit_service.dart';
|
import 'package:mileograph_flutter/services/distance_unit_service.dart';
|
||||||
|
import 'package:mileograph_flutter/services/authservice.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
|
|||||||
@@ -569,6 +569,7 @@ class _TractionPageState extends State<TractionPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildHeaderActions(BuildContext context, bool isMobile) {
|
Widget _buildHeaderActions(BuildContext context, bool isMobile) {
|
||||||
|
final isElevated = context.read<AuthService>().isElevated;
|
||||||
final refreshButton = IconButton(
|
final refreshButton = IconButton(
|
||||||
tooltip: 'Refresh',
|
tooltip: 'Refresh',
|
||||||
onPressed: _refreshTraction,
|
onPressed: _refreshTraction,
|
||||||
@@ -587,32 +588,34 @@ class _TractionPageState extends State<TractionPage> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
final newTractionButton = FilledButton.icon(
|
final newTractionButton = !isElevated
|
||||||
onPressed: () async {
|
? null
|
||||||
final createdClass = await context.push<String>(
|
: FilledButton.icon(
|
||||||
'/traction/new',
|
onPressed: () async {
|
||||||
);
|
final createdClass = await context.push<String>(
|
||||||
if (!mounted) return;
|
'/traction/new',
|
||||||
if (createdClass != null && createdClass.isNotEmpty) {
|
);
|
||||||
_classController.text = createdClass;
|
if (!mounted) return;
|
||||||
_selectedClass = createdClass;
|
if (createdClass != null && createdClass.isNotEmpty) {
|
||||||
_refreshTraction();
|
_classController.text = createdClass;
|
||||||
} else if (createdClass == '') {
|
_selectedClass = createdClass;
|
||||||
_refreshTraction();
|
_refreshTraction();
|
||||||
}
|
} else if (createdClass == '') {
|
||||||
},
|
_refreshTraction();
|
||||||
icon: const Icon(Icons.add),
|
}
|
||||||
label: const Text('New Traction'),
|
},
|
||||||
);
|
icon: const Icon(Icons.add),
|
||||||
|
label: const Text('New Traction'),
|
||||||
|
);
|
||||||
|
|
||||||
final desktopActions = [
|
final desktopActions = [
|
||||||
refreshButton,
|
refreshButton,
|
||||||
if (classStatsButton != null) classStatsButton,
|
if (classStatsButton != null) classStatsButton,
|
||||||
newTractionButton,
|
if (newTractionButton != null) newTractionButton,
|
||||||
];
|
];
|
||||||
|
|
||||||
final mobileActions = [
|
final mobileActions = [
|
||||||
newTractionButton,
|
if (newTractionButton != null) newTractionButton,
|
||||||
if (classStatsButton != null) classStatsButton,
|
if (classStatsButton != null) classStatsButton,
|
||||||
refreshButton,
|
refreshButton,
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -350,6 +350,7 @@ class HomepageStats {
|
|||||||
final List<YearlyMileage> yearlyMileage;
|
final List<YearlyMileage> yearlyMileage;
|
||||||
final List<LocoSummary> topLocos;
|
final List<LocoSummary> topLocos;
|
||||||
final List<LeaderboardEntry> leaderboard;
|
final List<LeaderboardEntry> leaderboard;
|
||||||
|
final List<LeaderboardEntry> friendsLeaderboard;
|
||||||
final List<TripSummary> trips;
|
final List<TripSummary> trips;
|
||||||
final int legCount;
|
final int legCount;
|
||||||
final UserData? user;
|
final UserData? user;
|
||||||
@@ -359,6 +360,7 @@ class HomepageStats {
|
|||||||
required this.yearlyMileage,
|
required this.yearlyMileage,
|
||||||
required this.topLocos,
|
required this.topLocos,
|
||||||
required this.leaderboard,
|
required this.leaderboard,
|
||||||
|
required this.friendsLeaderboard,
|
||||||
required this.trips,
|
required this.trips,
|
||||||
required this.legCount,
|
required this.legCount,
|
||||||
this.user,
|
this.user,
|
||||||
@@ -370,6 +372,17 @@ class HomepageStats {
|
|||||||
final totalMileage = mileageData is Map && mileageData['mileage'] != null
|
final totalMileage = mileageData is Map && mileageData['mileage'] != null
|
||||||
? (mileageData['mileage'] as num).toDouble()
|
? (mileageData['mileage'] as num).toDouble()
|
||||||
: 0.0;
|
: 0.0;
|
||||||
|
List<LeaderboardEntry> parseLeaderboard(dynamic raw) {
|
||||||
|
if (raw is List) {
|
||||||
|
return raw
|
||||||
|
.whereType<Map>()
|
||||||
|
.map((e) => LeaderboardEntry.fromJson(
|
||||||
|
e.map((k, v) => MapEntry(k.toString(), v)),
|
||||||
|
))
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
return const [];
|
||||||
|
}
|
||||||
return HomepageStats(
|
return HomepageStats(
|
||||||
totalMileage: totalMileage,
|
totalMileage: totalMileage,
|
||||||
yearlyMileage: (json['yearly_mileage'] as List? ?? [])
|
yearlyMileage: (json['yearly_mileage'] as List? ?? [])
|
||||||
@@ -378,9 +391,9 @@ class HomepageStats {
|
|||||||
topLocos: (json['top_locos'] as List? ?? [])
|
topLocos: (json['top_locos'] as List? ?? [])
|
||||||
.map((e) => LocoSummary.fromJson(e))
|
.map((e) => LocoSummary.fromJson(e))
|
||||||
.toList(),
|
.toList(),
|
||||||
leaderboard: (json['leaderboard_data'] as List? ?? [])
|
leaderboard: parseLeaderboard(json['leaderboard_data'] ?? json['leaderboard']),
|
||||||
.map((e) => LeaderboardEntry.fromJson(e))
|
friendsLeaderboard:
|
||||||
.toList(),
|
parseLeaderboard(json['friends_leaderboard'] ?? json['friendsLeaderboard']),
|
||||||
trips: (json['trip_data'] as List? ?? [])
|
trips: (json['trip_data'] as List? ?? [])
|
||||||
.map((e) => TripSummary.fromJson(e))
|
.map((e) => TripSummary.fromJson(e))
|
||||||
.toList(),
|
.toList(),
|
||||||
|
|||||||
@@ -169,10 +169,12 @@ class DataService extends ChangeNotifier {
|
|||||||
_homepageStats = HomepageStats.fromJson(json);
|
_homepageStats = HomepageStats.fromJson(json);
|
||||||
_trips = [...(_homepageStats?.trips ?? const [])]
|
_trips = [...(_homepageStats?.trips ?? const [])]
|
||||||
..sort(TripSummary.compareByDateDesc);
|
..sort(TripSummary.compareByDateDesc);
|
||||||
|
_friendsLeaderboard = _homepageStats?.friendsLeaderboard ?? [];
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
debugPrint('Failed to fetch homepage stats: $e');
|
debugPrint('Failed to fetch homepage stats: $e');
|
||||||
_homepageStats = null;
|
_homepageStats = null;
|
||||||
_trips = [];
|
_trips = [];
|
||||||
|
_friendsLeaderboard = [];
|
||||||
} finally {
|
} finally {
|
||||||
_isHomepageLoading = false;
|
_isHomepageLoading = false;
|
||||||
_notifyAsync();
|
_notifyAsync();
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev
|
|||||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||||
# In Windows, build-name is used as the major, minor, and patch parts
|
# In Windows, build-name is used as the major, minor, and patch parts
|
||||||
# of the product and file versions while build-number is used as the build suffix.
|
# of the product and file versions while build-number is used as the build suffix.
|
||||||
version: 0.6.2+5
|
version: 0.6.3+6
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ^3.8.1
|
sdk: ^3.8.1
|
||||||
|
|||||||
Reference in New Issue
Block a user