Track every release, fix, and improvement in Lamat
/privacy), Your Privacy Choices
(/privacy-choices), Cookie Policy
(/cookies) and Help & Support with a FAQ
section (/support). They are open to everyone —
no login needed — and are styled to match the site branding.
?page=pp, ?page=cookies and
?page=faq now redirect to the new public pages
instead of opening the landing page.
public_pages table stores each page's slug,
title and HTML content, and comes pre-filled with sensible
default Privacy, Privacy Choices, Cookies and Support text —
ready to be customised.
/apple/callback) that
Apple redirects to after login. On Android it safely hands
Apple's response back to the app so login completes
automatically; everywhere else it sends users back to the web
app.
https://lamatt.abbble.co.za/apple/callback
and https://lamat.abbble.co.za/) on the Service
ID.
lib/widgets so they can be reused more easily.
kreait/laravel-firebase ^7.2 (FCM HTTP v1 via
service account); enabled ext-sodium in
C:\xampp\php\php.ini (required by
lcobucci/jwt).
FIREBASE_CREDENTIALS pointing at
storage/app/firebase/service-account.json.
send() now prefers the FCM HTTP v1 API
(CloudMessage) when a service account is present,
and falls back to the legacy server-key API on failure or when
no service account exists; added
hasServiceAccount(); public method signatures
unchanged.
sendRealMessage() no longer requires
fcm_server_key when a Firebase service account is
configured; updated error message.
.env*, .git,
storage/app/firebase, app/,
config/, database/,
vendor/, tests/,
bootstrap/ now return 403 (docroot is the project
root, so these were previously served raw — the Firebase
service account and .env were publicly
downloadable).
Require all denied scoped to the service account
directory.
CallController passes ints, which previously
forced a v1 TypeError and silent legacy fallback);
hasServiceAccount() accepts absolute credential
paths.
storage/ deny was breaking uploaded
images served through the public/storage junction
to storage/app/public.
storage/app/firebase only (plus the original
non-storage entries).
Require all denied.
#manual-error alert — the actual server error
message is now rendered (previously dropped), and the output
log auto-reveals with a red border on failure.
?action=check/install/status handlers ran after
the HTML was echoed, so responses started with
<!DOCTYPE and failed
r.json() ("Unexpected token '<'"). All three
handlers now run at the top of the script, before any output
or the vendor-exists redirect.
?action=check
removed — the flow starts directly at "Install dependencies"
(2 steps: install → complete).
runCommand() accepts unset by-ref args (fixed
TypeError); Windows uses cmd /s /c for quote-safe
launching with output captured; Linux/macOS uses
nohup so composer survives request end; Windows
status detects php.exe via tasklist;
--no-ansi keeps logs clean everywhere.
searchTracks() and getTrack().
GET /spotify/search,
POST /spotify/set-anthem,
DELETE /spotify/remove-anthem.
spotify_client_id,
spotify_client_secret, and
spotify_enabled settings.
SpotifyAnthemPlugin model and registered in
Plugins class.
format=json&v=1.2 to WebSocket URL. Ably
defaults to binary msgpack encoding — without this param, all
frames arrived as binary and failed JSON parsing.
clientId field to CONNECT message for proper
token authentication.
keyName from full apiKey for token request URL
(was using full key with secret in URL path, causing 404).
throttle:10,1 to ably/token endpoint to prevent
abuse.
chat_msg_{userId}_{peerId}.
cachedMessagesPrefix to
HiveConstants.
since param
feature
since param — filters by
time > since OR edited_at > since; includes
recently deleted messages for client-side cache cleanup.
since query param and passes to service.
deleteForEveryone now sets
edited_at = now() so deletes are detected by
incremental sync.
since to fetch only changes.
AblyRealtime.enabled from config model in
connectAbly(); only connects when plugin is
enabled.
setEnabled() setter — early return in
connect() when disabled; no connection attempt.
full_name (givenName + familyName) from Apple
credential to the backend
full_name param and use it for user creation
instead of deriving from email prefix
openssl_verify() using
Apple's JWK public keys (previously only base64-decoded the
payload with no cryptographic verification)
exp (not expired),
iss (must be
https://appleid.apple.com),
aud (must match bundle ID)
rsaJwkToPem, base64urlDecode,
derLength helpers for JWT verification
sendMessage
ably_api_key,
ably_channel_prefix, ably_enabled)
10.29.0 to 12.16.0 (matches Firebase
iOS SDK v12.16.0)
pickFirst '**/*.so' to specific libc++_shared.so
per arch
splashBgDark field to AppImagesIcons
splashBgDark in initialize()
splash_bg_dark to plugins_settings for "App
Images Icons"
'storage/' . $path with
asset('storage/' . $path) so photo URLs stored in
DB are absolute URLs
user_id from
addInterest validator (Flutter never sends it;
endpoint already uses auth()->id())
TokenService.saveToken() after registration —
add_interest and getUser calls were
unauthenticated
DB::transaction() for atomicity
jpeg,png,jpg,gif,webp, max 10MB, max 8 images)
.toSafeString()/.toSafeInt()/.toSafeDouble()/.toSafeBool()
extension calls on dynamic receivers with
safeStr()/safeInt()/safeDouble()/safeBool()
top-level helpers
safeStr/safeInt/safeDouble/safeBool top-level
helpers that cast to Object? before calling
extension methods (fixes NoSuchMethodError on dynamic
receivers)
boolFromJson(dynamic value) to use
safeStr(value) instead of
value.toSafeString(); fixed
x.toSafeString() in .map() callback
to use safeStr(x)
SentryFlutter.init() in
try-catch with fallback
runApp(buildApp()) when Sentry fails; extracted
buildApp() widget function
assets/fonts/)
fontFamily from 'Lexend' to
'Outfit' (bundled); removed unused
google_fonts import
canvaskit.js/wasm for offline web rendering
window.flutterConfig.canvasKitBaseUrl pointing to
local /canvaskit/
GoogleFonts.outfit().fontFamily with 'Lexend' to
eliminate CDN font fetch on dark theme; removed
google_fonts import
<meta name="flutter-renderer" content="html">
to use HTML renderer (avoids canvaskit.js CDN download)
--web-renderer=html to all launch configs
BottomNavBarRoute path from '/' to
'/tabs' to prevent URL collision with initial
browser URL
id from getFavSounds validator (uses
auth()->id())
uid from addFavSound validator (uses
auth()->id())
singer and song_title to
getFeeds() response (were selected but not
returned)
audio_link and audio_cover during
createReel()
url('storage/' . $path)
flutter test
@visibleForTesting static setDioForTesting(Dio)
setter for injectable mock Dio
FormData from
universal_html/html.dart to fix import name
conflict with dio FormData
--force-jit flag required on Dart 3.10.7; use
dart run build_runner build --force-jit
main.dart.js; added
preconnect / dns-prefetch
for Google Fonts, Google Maps, Apple CDN
mockito from dependencies to
dev_dependencies (test-only)
@$!%*?& only)
show() now uses auth()->id() instead
of requiring id request param — fixes "Baking
Your Profile" hang when getUser() omitted the id
to hang indefinitely
borderImageUrl and
isSpeaking params for border decoration
ppBorders), host crown badge, floating gift
diamond button, chat overlay, and "Send Gift" option in member
list context menu; changed from
pushReplacement to direct
Stack overlay for custom UI on top of Zego
_InterestsSection with gradient-ring
avatars, tag labels, and mini avatars
_SpaceshipRocket,
_RocketPainter,
_FeaturedFramePainter, _TagPill,
_SignalBars, _MiniAvatars shared
widgets
BroadcastRoomCard widget
BroadcastRoomCard at index 3 in
ListLiveStreamSearch list view
host.soundLevel stream
_showGiftSheet gifting integration +
_SoundWaveWidget real-time audio visualization
default_home_page to
plugins_settings (General Settings) — value can
be swipecards, livestreams,
livemingle, voiceparty,
popballoon, hottakes,
blinddate, or reels
defaultHomePage field to
Settings class with fromJson/toJson
_BottomNavBarItem now reads
configs.plugins?.settings?.defaultHomePage
and renders the matching page widget dynamically
order column (int, default 0) to
plugins_settings
block group names (brand, currency, appearance,
registration, urls, profile, location, datetime, messages,
notifications, live, misc)
loadSettings() now orders by
order column instead of id
as String /
as String? → safeStr()
(~65 replacements across auth_user, match_model, streak_model,
question_model, user_photo, auth_providers, product_provider,
contests_provider, blind_date_provider, notification_service,
session_manager, live_mingle_search_screen, item_coin_plan,
user_profile_provider, voicechat_rooms_provider,
ranking_provider, boost_screen, live_stream_model,
other_profile_page, subscriptions)
.toInt() → safeInt()
(8 replacements across bank_card_model, stream_goal_model,
user_account_settings_model, top_search_model,
live_card_model, list_live_card_model)
int.parse() on map values →
safeInt()
(116 replacements across profile_controller,
user_profile_provider, other_users_provider,
voicechat_rooms_provider, teels_provider, ranking_provider)
double.parse() on map values →
safeDouble()
(19 replacements across profile_controller,
user_profile_provider, other_users_provider, coin_plans)
@RoutePage() wrapper around
image_editor_plus
@RoutePage() wrapper around
map_location_picker
/image/editor and
/location-picker
Navigator.push +
MaterialPageRoute with auto_route push;
backButton Navigator.pop() changed to
context.router.maybePop()
_listenToDiamondEvents() that subscribes to
pusherServiceProvider.onDiamondEarned and calls
DiamondAnimation.show() directly
StreamSubscription in dispose()
DiamondEventOverlay wrapper from Stack in build
method
diamond_event_provider import
Sentry.setUser() (undefined static method) with
Sentry.configureScope → scope.setUser()
BeforeSendCallback signature; changed named param
{hint} to positional hint (sentry
8.14.2 API)
SentryNavigatorObserver wrapper (NavigatorObserver ≠ Widget); observer already used in navigatorObservers list
All RevenueCat business logic extracted from Job into a testable service class. JSON error responses with UUID correlation IDs added for traceable debugging.
DB::table)
HandleRevenueCatWebhook to
webhooks queue via
->onQueue('webhooks')
GET /webhook/health) + try/catch
error tracking
origTransId,
productId, transactionId (trim +
truncate to column limits)
PRODUCT_CHANGE, TRANSFER,
SUBSCRIPTION_EXTENDED event types
$expirationMs →
$expirationAtMs (matches RC field name)
ConfigService::getRevenueCart →
getRevenueCat (typo)
initPlatformStateForPurchases (removed
userID! crash risk)
boost_balance sync from
subscription_builder.dart; webhook is sole grants
source
activateRevenueCatSubscription &
renewRevenueCatSubscription
isPremiumUserProvider uses
ref.watch (reactive) instead of
ref.read (snapshot)
handleRevenueCat (prevents orphan records)
premium_expiry_date type bug (int
→ MySQL datetime string)
POST /webhook/revenuecat route
handleRevenueCat() with HMAC-SHA256 signature
verification (V1/V2); resolves pro_plans by product_id;
handles
INITIAL_PURCHASE/RENEWAL/CANCELLATION/UNCANCELLATION/EXPIRATION
events; inserts/updates users_subscriptions + users tables
isPremiumUserProvider from
premiumCustomerInfoProvider to eliminate
duplicate Purchases.getCustomerInfo() calls;
added web guard
WidgetRef; added try-catch around RevenueCat
init; listener now syncs both activation AND deactivation with
early return when state matches
kIsWeb guard to return empty list without calling
Purchases.getOfferings()
ref.read → ref.watch for reactive
rebuilds; replaced FutureBuilder with try/await; removed app
restart and splash navigation after purchase
GET wallets/,
GET wallets/{type}, GET earnings
index(), show(),
getEarnings()
walletsProvider,
walletByTypeProvider
earningListProvider
computeWalletComputedTotals(): aggregates
balance, points, earnings, withdrawals, rewards, gifts,
deposits, transactions count from 6 real tables
formatWallet now accepts computed array param,
overrides all stored totals
getAllWallets /
getWalletByType pre-compute totals once before
formatting
GET withdrawals/ added to api.phpwithdrawalListProvider
2026_07_02_000001_create_users_wallets_table.php
2026_07_02_000002_create_users_earnings_table.php
Completed project-wide migration from raw
Navigator.push +
MaterialPageRoute to auto_route across 40+ files.
Added 7 missing route classes. Only third-party
MapLocationPicker remains unconverted.
_startTimer →
_startTimer() to actually start the countdown
timer
build() in blind_date_waiting.dart and
blind_date_lobby.dart; added _navigated flags
and transition guards to prevent duplicate navigations
_pollTimer?.cancel() in
reveal() and auto-stop polling when session
status is revealed/expired
Future.doWhile with
Timer.periodic + cancel in
dispose()
_init() in
addPostFrameCallback
_prefs from
nullable to late SharedPreferences
_onTimeUp now
navigates to BlindDateExpired (was incorrectly navigating to
BlindDateWaiting)
fetchEvents() in initState
Navigator.pushReplacement with
context.router.replace
blind_date_provider.dart from
echo_feed_page.dart
clearError bool with sentinel pattern in
copyWith
EasyLoading.showError in
pollStatus catch block
Cameras start off; tap Reveal turns on MY camera (they see
me); poll detects mutual reveal; 2s delay then navigate to
BlindDateReveal. _onTimeUp now calls
skip() before navigating to properly end the Zego
call.
Replaced one-time
ref.read(blindDateProvider) with
ref.listen to reactively detect partner data;
_checkMatch now receives
matchInfo as parameter.
splash_gif_anim.dart — show anim from configs
(NetworkImage) with fallback to local asset (AssetImage) from
AppConstants.splashAnim.
PRODUCT-SRC-R/E-XXXXXXXX-XXXXXXXX-XXXXXXXX)
for all products
admin-list-source-purchases API endpoint
lamat_licenses table, all licenses now in
source_purchases
users table to
license_verifications
012_drop_lamat_licenses_table.sql,
013_rename_users_to_license_verifications.sql
Now creates timestamped folder at script location instead of hardcoded zip path; cross-platform (macOS, Windows via Git Bash/WSL).
users_swipe_preferences table (uid,
category, value with unique constraint)
updateUserSwipePreference route +
controller method (updateOrInsert pattern)
updateUserSwipePreference method to
UserProfileNotifier
Inserted 26 questions and 165 answer options across 4 batches covering: report reasons, here_to, religion, hair/eye/skin color, zodiac, education, personality, family_plans, drinking, smoking, working, diet, social_media_activity, sleeping_habits, going_out, weekends, phone habits, drugs, pets, children, love_language, has_baby, language, com_style.
appImagesIcons field to
Plugins class
getAppImagesIcons() method in
ConfigService.php
mobileAppColors field to
Plugins class
getMobileAppColors() method in
ConfigService.php
getSettingsPlugin() method
with all new config fields
getRewards() method with all
new reward credit fields
enabled and dsn fields
sentry field to
Plugins class
getSentry() method in
ConfigService.php
enabled, apple_api_key,
google_api_key, entitlement_id,
app_id fields
revenueCart field to
Plugins class
getRevenueCart() method in
ConfigService.php
enabled, android_*_id,
ios_*_id fields
admob field to
Plugins class
getAdmob() method in
ConfigService.php
fcm_server_key and
fcm_project_id to plugins_settings for the
OneSignal plugin
/push-notifications → PushNotifications
FcmService::sendChatMessage(),
OneSignalService::sendPromotional()/sendToUser()