mirror of
https://github.com/chenasraf/pantry-flutter.git
synced 2026-05-18 01:28:58 +00:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2545e421de | ||
| a5c8e5b479 | |||
| e69625e8af |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -55,6 +55,7 @@ android/app/*.keystore
|
||||
|
||||
# Fastlane
|
||||
android/fastlane/play-store-key.json
|
||||
android/fastlane/.image_hashes.json
|
||||
ios/fastlane/report.xml
|
||||
android/fastlane/report.xml
|
||||
/.envrc
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# Changelog
|
||||
|
||||
## [0.2.1](https://github.com/chenasraf/pantry-flutter/compare/v0.2.0...v0.2.1) (2026-04-11)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* sorting prefs persistence & error wrapping ([a5c8e5b](https://github.com/chenasraf/pantry-flutter/commit/a5c8e5b479e92f87ea910b5af19ca24711ce7b16))
|
||||
|
||||
## [0.2.0](https://github.com/chenasraf/pantry-flutter/compare/v0.1.0...v0.2.0) (2026-04-11)
|
||||
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
require "digest"
|
||||
require "json"
|
||||
|
||||
default_platform(:android)
|
||||
|
||||
platform :android do
|
||||
@@ -13,17 +16,24 @@ platform :android do
|
||||
"#{v[:build]} (#{v[:name]})"
|
||||
end
|
||||
|
||||
# Google Play enforces a 500-char limit on release notes per language.
|
||||
PLAY_NOTES_MAX = 500
|
||||
PLAY_NOTES_TRAILER = "\n\n… see full notes on GitHub."
|
||||
|
||||
def changelog_notes
|
||||
version = version_info[:name]
|
||||
changelog_path = File.expand_path("../../CHANGELOG.md", __dir__)
|
||||
return "Release #{version}" unless File.exist?(changelog_path)
|
||||
|
||||
changelog = File.read(changelog_path)
|
||||
pattern = /^## \[#{Regexp.escape(version)}\].*?\n(.*?)(?=^## \[|\z)/m
|
||||
# Match the release heading (both `## [x.y.z]...` and `## x.y.z ...` forms),
|
||||
# capture until the next `## ` heading (any form) or EOF.
|
||||
escaped = Regexp.escape(version)
|
||||
pattern = /^## (?:\[#{escaped}\]|#{escaped}[\s(]).*?\n(.*?)(?=^## |\z)/m
|
||||
match = changelog.match(pattern)
|
||||
return "Release #{version}" unless match
|
||||
|
||||
match[1].strip
|
||||
notes = match[1].strip
|
||||
.gsub(/\s*\(\[[\da-f]+\]\([^)]+\)\)/, "")
|
||||
.gsub(/^\*\s+\*\*[^*]+:\*\*\s*/m, "- ")
|
||||
.gsub(/^\*\s+/, "- ")
|
||||
@@ -31,6 +41,56 @@ platform :android do
|
||||
.gsub(/\n{3,}/, "\n\n")
|
||||
.strip
|
||||
.then { |n| n.empty? ? "Release #{version}" : n }
|
||||
|
||||
truncate_for_play(notes)
|
||||
end
|
||||
|
||||
# Truncate release notes to fit within the Play Store limit. If the
|
||||
# notes exceed the limit, trim to a line boundary and append a trailer
|
||||
# so the total stays under [PLAY_NOTES_MAX].
|
||||
def truncate_for_play(notes)
|
||||
return notes if notes.length <= PLAY_NOTES_MAX
|
||||
|
||||
budget = PLAY_NOTES_MAX - PLAY_NOTES_TRAILER.length
|
||||
truncated = notes[0, budget]
|
||||
|
||||
# Cut at the last newline so we don't chop a bullet mid-word.
|
||||
last_newline = truncated.rindex("\n")
|
||||
truncated = truncated[0, last_newline] if last_newline && last_newline > budget / 2
|
||||
|
||||
truncated.rstrip + PLAY_NOTES_TRAILER
|
||||
end
|
||||
|
||||
# -- Image change detection --
|
||||
# We hash every file under metadata/android/*/images/ and compare to a
|
||||
# cache file. If nothing changed, we skip image uploads entirely.
|
||||
|
||||
IMAGE_HASH_CACHE = File.expand_path(".image_hashes.json", __dir__)
|
||||
|
||||
def current_image_hashes
|
||||
root = File.expand_path("metadata/android", __dir__)
|
||||
return {} unless Dir.exist?(root)
|
||||
|
||||
files = Dir.glob(File.join(root, "**/images/**/*")).select { |f| File.file?(f) }
|
||||
files.sort.each_with_object({}) do |path, acc|
|
||||
rel = path.sub("#{root}/", "")
|
||||
acc[rel] = Digest::SHA256.file(path).hexdigest
|
||||
end
|
||||
end
|
||||
|
||||
def cached_image_hashes
|
||||
return {} unless File.exist?(IMAGE_HASH_CACHE)
|
||||
JSON.parse(File.read(IMAGE_HASH_CACHE))
|
||||
rescue StandardError
|
||||
{}
|
||||
end
|
||||
|
||||
def images_changed?
|
||||
current_image_hashes != cached_image_hashes
|
||||
end
|
||||
|
||||
def save_image_hashes
|
||||
File.write(IMAGE_HASH_CACHE, JSON.pretty_generate(current_image_hashes))
|
||||
end
|
||||
|
||||
desc "Upload AAB to Google Play"
|
||||
@@ -42,22 +102,36 @@ platform :android do
|
||||
FileUtils.mkdir_p(changelog_dir)
|
||||
File.write(File.join(changelog_dir, "#{version_code}.txt"), changelog_notes)
|
||||
|
||||
changed = images_changed?
|
||||
UI.message(changed ? "Images changed — uploading." : "Images unchanged — skipping.")
|
||||
|
||||
upload_to_play_store(
|
||||
aab: File.expand_path("../../build/app/outputs/bundle/release/app-release.aab", __dir__),
|
||||
track: options[:track] || "internal",
|
||||
release_status: options[:status] || "draft",
|
||||
version_name: version_name,
|
||||
metadata_path: File.expand_path("metadata/android", __dir__),
|
||||
skip_upload_images: !changed,
|
||||
skip_upload_screenshots: !changed,
|
||||
)
|
||||
|
||||
save_image_hashes if changed
|
||||
end
|
||||
|
||||
desc "Sync metadata and screenshots only (no AAB upload)"
|
||||
lane :metadata do
|
||||
changed = images_changed?
|
||||
UI.message(changed ? "Images changed — uploading." : "Images unchanged — skipping.")
|
||||
|
||||
upload_to_play_store(
|
||||
skip_upload_aab: true,
|
||||
skip_upload_apk: true,
|
||||
metadata_path: File.expand_path("metadata/android", __dir__),
|
||||
skip_upload_images: !changed,
|
||||
skip_upload_screenshots: !changed,
|
||||
)
|
||||
|
||||
save_image_hashes if changed
|
||||
end
|
||||
|
||||
desc "Promote a release from one track to another"
|
||||
|
||||
8
android/fastlane/metadata/android/en-US/changelogs/3.txt
Normal file
8
android/fastlane/metadata/android/en-US/changelogs/3.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
Features
|
||||
|
||||
- add sorting by category for checklist
|
||||
- notifications support
|
||||
|
||||
Bug Fixes
|
||||
|
||||
- add bottom padding to accomodate fab
|
||||
@@ -69,15 +69,15 @@ class ChecklistService {
|
||||
|
||||
Future<String> getItemSortPref(int houseId) async {
|
||||
return ApiClient.instance.get<Map<String, dynamic>, String>(
|
||||
'/houses/$houseId/prefs/checklist-item-sort',
|
||||
fromJson: (data) => data['sort'] as String? ?? 'custom',
|
||||
'/houses/$houseId/prefs',
|
||||
fromJson: (data) => data['checklistItemSort'] as String? ?? 'custom',
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> setItemSortPref(int houseId, String sort) async {
|
||||
await ApiClient.instance.put<Map<String, dynamic>, void>(
|
||||
'/houses/$houseId/prefs/checklist-item-sort',
|
||||
body: {'sort': sort},
|
||||
'/houses/$houseId/prefs',
|
||||
body: {'checklistItemSort': sort},
|
||||
fromJson: (_) {},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:pantry/i18n.dart';
|
||||
import 'package:pantry/models/category.dart' as models;
|
||||
@@ -167,8 +169,9 @@ class ChecklistsController extends ChangeNotifier {
|
||||
_checklistService.cache.set('sortBy', sort);
|
||||
notifyListeners();
|
||||
|
||||
// Persist to server
|
||||
_checklistService.setItemSortPref(houseId, sort);
|
||||
// Fire-and-forget the server persist so a slow or failing pref write
|
||||
// never blocks the item reload.
|
||||
unawaited(_persistSortPref(sort));
|
||||
|
||||
// Reload items with new sort
|
||||
if (_currentList != null) {
|
||||
@@ -177,6 +180,14 @@ class ChecklistsController extends ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _persistSortPref(String sort) async {
|
||||
try {
|
||||
await _checklistService.setItemSortPref(houseId, sort);
|
||||
} catch (e) {
|
||||
debugPrint('[ChecklistsController] Failed to persist sort pref: $e');
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> reorderItems(
|
||||
List<ListItem> partition,
|
||||
int oldIndex,
|
||||
|
||||
@@ -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
|
||||
# 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.
|
||||
version: 0.2.0+3
|
||||
version: 0.2.1+4
|
||||
|
||||
environment:
|
||||
sdk: ^3.11.1
|
||||
|
||||
Reference in New Issue
Block a user