Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
637 commits
Select commit Hold shift + click to select a range
b90703c
fix(spaces): visibly separate public space overview from its possibil…
Gilbert243 Oct 20, 2026
22b15a7
fix(discover): compact public exploration header while preserving res…
Gilbert243 Oct 20, 2026
6e8c61d
fix(spaces): show canonical archetype instead of geographic location …
Gilbert243 Oct 20, 2026
63429c0
style(mobile): align public Space archetype subtitle with Dart formatter
Gilbert243 Oct 20, 2026
28d2637
build(frontend): synchronize generated makolo.css with build output
Gilbert243 Oct 20, 2026
fbfb62b
build(frontend): synchronize generated makolo.js with build output
Gilbert243 Oct 20, 2026
b3e9743
chore(presentations): consolidate P2 media-use work into PR #522
Gilbert243 Oct 22, 2026
a4d0a45
chore(presentations): consolidate P2 media-use work into PR #522
Gilbert243 Oct 22, 2026
0c0f250
chore(presentations): consolidate P2 media-use work into PR #522
Gilbert243 Oct 22, 2026
22d4c86
chore(presentations): consolidate P2 media-use work into PR #522
Gilbert243 Oct 22, 2026
38f11e9
chore(presentations): consolidate P2 media-use work into PR #522
Gilbert243 Oct 22, 2026
2d6fc34
chore(presentations): consolidate P2 media-use work into PR #522
Gilbert243 Oct 22, 2026
01a8757
feat(presentations): register canonical P2 media-use relation
Gilbert243 Oct 22, 2026
c6ca0d7
fix(security): consolidate audited dependency updates from PR #525
Gilbert243 Oct 22, 2026
10bd743
fix(home): keep opaque action identifiers out of presentation repr
Gilbert243 Oct 22, 2026
9e27f76
fix(discover): remove uncontracted preview action from N1 cards
Gilbert243 Oct 22, 2026
e9ade45
fix(mobile): remove unused legacy public-space website handler
Gilbert243 Oct 22, 2026
8f65783
fix(mobile): remove orphan website helper and sharing import
Gilbert243 Oct 22, 2026
ab2af0d
fix(mobile): surface contextual public error instead of generic failure
Gilbert243 Oct 22, 2026
ae94178
fix(mobile): surface contextual public error instead of generic failure
Gilbert243 Oct 22, 2026
beba359
test(mobile): scroll expanded description collapse control into view …
Gilbert243 Oct 22, 2026
6a79d84
feat(presentations): serve published media uses with ownership and gr…
Gilbert243 Oct 22, 2026
6a83103
feat(presentations): route authorized media-use delivery
Gilbert243 Oct 22, 2026
5128a1b
feat(discovery): project authorized presentation media uses into N1 a…
Gilbert243 Oct 22, 2026
0c42b4e
feat(presentations): add template-free media upload and retirement wo…
Gilbert243 Oct 22, 2026
c0a985c
feat(presentations): route media management actions
Gilbert243 Oct 22, 2026
e35de2c
feat(presentations): show media associations in existing Studio
Gilbert243 Oct 22, 2026
16d685e
feat(presentations): provide N1 media upload, publish and retire cont…
Gilbert243 Oct 22, 2026
56a7835
feat(mobile): resolve server-scoped image URLs in N2 without inventin…
Gilbert243 Oct 22, 2026
4ca745a
test(presentations): P5 media HTTP publication, revocation and missin…
Gilbert243 Oct 22, 2026
a82336d
fix(presentations): require public eligible owner before delivering p…
Gilbert243 Oct 22, 2026
9771258
test(presentations): establish explicitly public owner in media deliv…
Gilbert243 Oct 22, 2026
edea537
docs(makolo): P2-P5 N1 media implementation, acceptance and beta APK …
Gilbert243 Oct 22, 2026
1a51482
fix(discovery): bound explicit media bindings and preserve specific-f…
Gilbert243 Oct 22, 2026
ded63be
refactor(discovery): pure N1 binding composition with occurrence scoping
Gilbert243 Oct 22, 2026
c966f1e
refactor(discovery): use independently testable N1 media binding comp…
Gilbert243 Oct 22, 2026
897deb4
test(discovery): P5 N1 published image, scope and no-media regressions
Gilbert243 Oct 22, 2026
b472cb3
feat(presentations): isolate new visual assets from public MEDIA_ROOT
Gilbert243 Oct 22, 2026
59764d8
feat(presentations): keep legacy asset path and add protected storage…
Gilbert243 Oct 22, 2026
af88616
feat(presentations): additive migration for private N1 visual asset s…
Gilbert243 Oct 22, 2026
c1d8df2
feat(presentations): import new N1 media into protected storage expli…
Gilbert243 Oct 22, 2026
6f0b53f
feat(presentations): make Studio N1 uploads private by default
Gilbert243 Oct 22, 2026
dfd166e
fix(presentations): validate protected or legacy bytes at publication
Gilbert243 Oct 22, 2026
6b5d1ed
fix(presentations): stream N1 images from protected storage when avai…
Gilbert243 Oct 22, 2026
0dce3a2
docs(presentations): record private N1 file storage and additive migr…
Gilbert243 Oct 22, 2026
b35b201
docs(presentations): record private N1 file storage and additive migr…
Gilbert243 Oct 22, 2026
8ae5dba
test(presentations): protected asset import and controlled publicatio…
Gilbert243 Oct 22, 2026
df69278
test(presentations): use actual PNG signature bytes in protected impo…
Gilbert243 Oct 23, 2026
a3ad019
fix(mobile): restore actionable N1 title and visible canonical facts …
Gilbert243 Oct 23, 2026
b581602
fix(mobile): restore actionable N1 title and visible canonical facts …
Gilbert243 Oct 23, 2026
1bfcc02
test(mobile): exercise N1 lazy scroll before asserting subsequent rea…
Gilbert243 Oct 23, 2026
f8c1398
fix(mobile): expand Discovery description on text tap and avoid dupli…
Gilbert243 Oct 23, 2026
de3b059
test(mobile): cover tap-to-expand and tap-to-collapse Discovery descr…
Gilbert243 Oct 23, 2026
a488f1b
fix(funding): use Decimal minimums in DRF serializers
Gilbert243 Oct 23, 2026
f86d716
fix(discovery): make public resources actionable and guard downloads
Gilbert243 Oct 23, 2026
a2abc1b
feat(discovery-mobile): open resources and browse image gallery
Gilbert243 Oct 23, 2026
54493e3
feat(preparation): validate documentary CSV, MP4 and WAV resources
Gilbert243 Oct 23, 2026
5300f66
feat(discovery): reproducible authorized media demo fixtures
Gilbert243 Oct 23, 2026
56e429a
test(discovery): cover public resource previews and demo documentary …
Gilbert243 Oct 23, 2026
ddd5746
fix(discovery-web): preserve media aspect ratio and open original
Gilbert243 Oct 23, 2026
1262868
test(discovery): assert real demo media, download authorization and i…
Gilbert243 Oct 23, 2026
663788c
style(mobile): apply canonical Dart formatting
somafgroup Oct 24, 2026
e46d0c5
feat(discovery-demo): safely rebalance synthetic image uses
Gilbert243 Oct 24, 2026
7003257
feat(discovery-demo): authorized retirement and diverse resource fixt…
Gilbert243 Oct 24, 2026
13fced3
test(discovery-demo): read-only audit of persisted media bytes
Gilbert243 Oct 24, 2026
e7f6232
fix(discovery): reconcile missing demo files and close CI build/secur…
Gilbert243 Oct 24, 2026
abae984
feat(discovery-demo): import authorized synthetic ZIP without extra s…
Gilbert243 Oct 24, 2026
3d5d023
test(discovery): use actual binary signatures in ZIP import fixtures
Gilbert243 Oct 24, 2026
6c93f8d
fix(discovery-demo): snapshot files before retirement on SQLite
Gilbert243 Oct 24, 2026
a47b1a4
feat(discovery): introduce internal transient media and document reader
Gilbert243 Oct 24, 2026
d424e22
feat(discovery): open primary gallery and resources inside Makolo
Gilbert243 Oct 24, 2026
8a7163c
test(discovery): guard embedded destinations and bound N1 text previews
Gilbert243 Oct 24, 2026
f322ee7
feat(discovery): add bounded in-app offline storage for public media
Gilbert243 Oct 24, 2026
d5b7db6
feat(discovery): retain and reopen public resources inside Makolo off…
Gilbert243 Oct 24, 2026
050dcef
feat(discovery): consult N1 media and full text in both Web and Flutter
Gilbert243 Oct 24, 2026
5a96f6a
test(discovery): protect full text access and resilient offline reade…
Gilbert243 Oct 24, 2026
4b6f416
fix(discovery): validate reader content types and make CSV inline
Gilbert243 Oct 24, 2026
fe8b7a9
fix(mobile): open Discovery external links directly in in-app browser
Gilbert243 Oct 24, 2026
fa95f18
fix(preparation): serve CSV resources as attachments
Gilbert243 Oct 24, 2026
aba79b0
style(mobile): make Discovery resource labels the Makolo-colored links
Gilbert243 Oct 24, 2026
ff3aca6
presentation files
somafgroup Oct 24, 2026
dee2767
fix(mobile): use media-specific resource icons and shared discovery s…
Gilbert243 Oct 24, 2026
62fa13a
style(mobile): remove discovery resource link underlines
Gilbert243 Oct 24, 2026
945929e
feat(mobile): compose discovery N1 metadata, descriptions and resources
Gilbert243 Oct 24, 2026
956253b
fix(mobile): keep resource expansion control outside resource rows
Gilbert243 Oct 24, 2026
ef4789a
test(mobile): cover N1 resource overflow and three-line descriptions
Gilbert243 Oct 24, 2026
42d5ac3
refactor(mobile): align N1 supplementary context and grouped resources
Gilbert243 Oct 24, 2026
2704fd1
style(mobile): tighten discovery description toggle vertical spacing
Gilbert243 Oct 24, 2026
04ea13d
fix(mobile): persist public Discovery and Spaces snapshots for offlin…
Gilbert243 Oct 24, 2026
61cca0e
fix(mobile): keep cached public content unobstructed during outages
Gilbert243 Oct 24, 2026
6c993a2
chore(ci): synchronize Dart formatting and generated CSS artifacts
Gilbert243 Oct 24, 2026
25f3030
fix(ci): preserve minified CSS exact no-newline output
Gilbert243 Oct 24, 2026
d8a5532
feat(mobile): share Discovery resource links directly from reader
Gilbert243 Oct 24, 2026
40cf36f
style(mobile): align Discovery date with owner and compose contextual…
Gilbert243 Oct 24, 2026
05ae06a
fix(mobile): keep nullable owner safely guarded in header
Gilbert243 Oct 24, 2026
7e77872
style(web): show Discovery addition date beside public owner
Gilbert243 Oct 24, 2026
5a36911
style(mobile): use stable absolute addition date in Discovery header
Gilbert243 Oct 24, 2026
2259d49
test(mobile): cover owner-date header and contextual Discovery labels
Gilbert243 Oct 24, 2026
209284d
fix(discovery): expose all authorized public resources without six-it…
Gilbert243 Oct 24, 2026
e97b409
refactor(mobile): keep canonical Discovery facts stable and separated…
Gilbert243 Oct 24, 2026
4ab90fe
feat(mobile): place all accessible annexes after media with two-row d…
Gilbert243 Oct 24, 2026
013b971
style(mobile): fix N1 order and keep practical facts independent from…
Gilbert243 Oct 24, 2026
3a5b0a2
refactor(web): extract reusable Discovery resource row for bounded in…
Gilbert243 Oct 24, 2026
6ec9e82
refactor(discovery web): extract accessible annex resource presentation
Gilbert243 Oct 24, 2026
862da66
style(web): align Discovery N1 semantic order and disclose annexes af…
Gilbert243 Oct 24, 2026
51a9762
refactor(web): map Discovery type, facts and localized addition date …
Gilbert243 Oct 24, 2026
aba2ba2
chore(discovery): remove unused resource partial after concurrent Web…
Gilbert243 Oct 24, 2026
b80aef2
test(discovery): assert two initial resources and all seven available…
Gilbert243 Oct 24, 2026
5e03152
test(discovery): verify all eight public resources and private exclus…
Gilbert243 Oct 24, 2026
e58751e
test(mobile): lock seven annexes and independently visible practical …
Gilbert243 Oct 24, 2026
fa4e475
style(discovery web): keep annex rows compact; reveal long text on de…
Gilbert243 Oct 24, 2026
20e4dcb
fix(discovery mobile): make annex affordances reflect real authorized…
Gilbert243 Oct 24, 2026
340f8e1
test(discovery): ensure practical facts stay stable across descriptio…
Gilbert243 Oct 24, 2026
04d7fba
fix(discovery web): preserve full text on expand even without a separ…
Gilbert243 Oct 24, 2026
b83fc28
fix(web): toggle full Discovery text without repeating its preview
Gilbert243 Oct 24, 2026
380b429
testability(mobile): mark Discovery primary media and gallery for lay…
Gilbert243 Oct 24, 2026
cb07c72
test(mobile): lock N1 section order from title through actions
Gilbert243 Oct 24, 2026
e9258ca
test(mobile): allow missing-image zero-height fallback in media order…
Gilbert243 Oct 24, 2026
4c10b02
docs(discovery): freeze N1 information order and all-resource disclos…
Gilbert243 Oct 24, 2026
3b0763a
fix(mobile discovery): remove verbose cache warning and replace quest…
Gilbert243 Oct 24, 2026
a429c50
style(mobile): allow quiet single-line recoverable status instead of …
Gilbert243 Oct 24, 2026
34381c8
test(mobile): assert concise Discovery heading without tutorial question
Gilbert243 Oct 24, 2026
dbcdab5
style(discovery): concise retry state and clear domain-oriented intro…
Gilbert243 Oct 24, 2026
da2096d
refactor(mobile): keep generic surface notice unchanged; Discovery us…
Gilbert243 Oct 24, 2026
a7dfe88
copy(mobile): concise domain language for discovery
Gilbert243 Oct 24, 2026
ab2319d
copy(mobile): concise domain language for now
Gilbert243 Oct 24, 2026
53fbd56
copy(mobile): concise domain language for ongoing
Gilbert243 Oct 24, 2026
becc5bb
test(mobile): align Discovery assertions with concise business copy
Gilbert243 Oct 24, 2026
95bc571
copy(mobile): streamline labels and states in discovery
Gilbert243 Oct 24, 2026
f06f5ce
copy(mobile): streamline labels and states in me
Gilbert243 Oct 24, 2026
7f6e98d
copy(mobile): streamline labels and states in ongoing
Gilbert243 Oct 24, 2026
c177679
fix(mobile): floating retry feedback with existing public content
Gilbert243 Oct 24, 2026
145eaff
copy(mobile): concise actionable messages without technical explanations
Gilbert243 Oct 25, 2026
d25dce7
copy(mobile): concise actionable messages without technical explanations
Gilbert243 Oct 25, 2026
886d392
copy(mobile): concise actionable messages without technical explanations
Gilbert243 Oct 25, 2026
26d63a6
copy(me): remove tutorial explanations from details and shorten unava…
Gilbert243 Oct 25, 2026
319455c
style(me): keep detail layout formatted after copy cleanup
Gilbert243 Oct 25, 2026
7694415
fix(mobile): avoid duplicate refresh warnings above usable personal c…
Gilbert243 Oct 25, 2026
15677f7
fix(mobile): avoid duplicate refresh warnings above usable personal c…
Gilbert243 Oct 25, 2026
944fa83
feat(discovery): select canonical primary engagement action on server
Gilbert243 Oct 25, 2026
dd59b19
feat(discovery mobile): use server-selected primary action for center…
Gilbert243 Oct 25, 2026
11fe585
test(discovery mobile): center action honors server detail fallback
Gilbert243 Oct 25, 2026
b397862
docs(discovery): freeze N1/N2 primary action contract and server Web …
Gilbert243 Oct 25, 2026
5f55155
fix(discovery): only execute server-approved native primary navigatio…
Gilbert243 Oct 25, 2026
2fff78b
test(discovery): verify owner-approved tickets and safe detail fallba…
Gilbert243 Oct 25, 2026
4384c3c
test(discovery): cover authenticated owner-approved Service start action
Gilbert243 Oct 25, 2026
e3a12d1
feat(discovery): continue existing owner Web actions when no native r…
Gilbert243 Oct 25, 2026
5d52117
test(discovery): exercise owner Web continuation and reject foreign a…
Gilbert243 Oct 25, 2026
a9c8d0e
feat(discovery): expose verified existing owner Web continuations for…
Gilbert243 Oct 25, 2026
85f0d2d
fix(discovery): reject all backslash variants in Web action paths
Gilbert243 Oct 25, 2026
5b8ba31
fix(discovery mobile): honor typed server Web action and exact owner …
Gilbert243 Oct 25, 2026
ade428f
test(discovery mobile): require matching typed owner Web action
Gilbert243 Oct 25, 2026
9aa427a
test(discovery api): verify Web owner action and safe detail fallback
Gilbert243 Oct 25, 2026
29d18b5
fix(discovery mobile): do not leave Flutter for Web owner actions
Gilbert243 Oct 25, 2026
a5131f7
feat(mobile tickets): sandbox payment with existing Payment owner API
Gilbert243 Oct 25, 2026
0243dd6
fix(mobile payment): prevent repeat sandbox payment after success and…
Gilbert243 Oct 25, 2026
7242873
feat(funding api): expose minimal authorized public details for nativ…
Gilbert243 Oct 25, 2026
7afe69a
feat(funding api): register public native detail path
Gilbert243 Oct 25, 2026
44fa111
feat(payments api): validate obligation payment initiation using owne…
Gilbert243 Oct 25, 2026
b569705
feat(payments api): expose permission-checked obligation details and …
Gilbert243 Oct 25, 2026
01fda68
feat(payments api): register owner obligation mobile endpoint
Gilbert243 Oct 25, 2026
271812a
feat(discovery): link Funding candidates to native contribution owner…
Gilbert243 Oct 25, 2026
2f861ab
feat(mobile funding): native contribution screen using existing Fundi…
Gilbert243 Oct 25, 2026
ce91dd4
feat(mobile payments): native PaymentObligation and sandbox execution…
Gilbert243 Oct 25, 2026
efb9ba9
feat(mobile routing): native Funding and PaymentObligation routes
Gilbert243 Oct 25, 2026
a43fadc
feat(discovery mobile): route Funding action natively using owner-ver…
Gilbert243 Oct 25, 2026
03a0055
fix(funding): recheck group eligibility before native contribution mu…
Gilbert243 Oct 25, 2026
bfb6815
fix(payments api): enforce payer authority on obligation idempotent r…
Gilbert243 Oct 25, 2026
fc26d88
fix(discovery): remove Web escape hatch from native primary action co…
Gilbert243 Oct 25, 2026
a72ae4e
test(discovery): native-only primary actions and Funding handoff
Gilbert243 Oct 25, 2026
520eed1
test(mobile N1): Funding native action and never navigate to Web
Gilbert243 Oct 25, 2026
18b4ea0
test(funding payments): public native details, owner obligation and i…
Gilbert243 Oct 25, 2026
78528d3
feat(mobile events): native owner waitlist join when ticket type is s…
Gilbert243 Oct 25, 2026
9bc505e
test(payments): assert owner obligation satisfied only after sandbox …
Gilbert243 Oct 25, 2026
949d594
fix(mobile funding): freeze idempotent contribution amount during unc…
Gilbert243 Oct 25, 2026
431269e
fix(mobile payments): hide repeat confirmation after successful owner…
Gilbert243 Oct 25, 2026
ab9b276
feat(discovery): owner-verified Obtention native action for published…
Gilbert243 Oct 25, 2026
06a677e
feat(mobile obtention): native mode selection then canonical Journey …
Gilbert243 Oct 25, 2026
ddc1fed
feat(mobile routing): native Obtention entry using existing Journey d…
Gilbert243 Oct 25, 2026
95e838e
feat(discovery mobile): open native Obtention contextual flow from N1
Gilbert243 Oct 25, 2026
7e450ba
fix(obtention): check group eligibility before native public view or …
Gilbert243 Oct 25, 2026
4ed075d
test(discovery): verify Obtention native owner continuation
Gilbert243 Oct 25, 2026
de6f40d
test(mobile N1): Obtention uses typed native action rather than Start
Gilbert243 Oct 25, 2026
73107c4
docs(discovery): record native Service Event Funding Obtention and Pa…
Gilbert243 Oct 25, 2026
6661f6b
feat(commerce): expose owner-checked personal Journey checkout API
Gilbert243 Oct 25, 2026
dbc0cb8
feat(discovery): prefer owner-confirmed Commerce orders and active Jo…
Gilbert243 Oct 25, 2026
fda55c0
feat(mobile): native Commerce checkout and resumable order detail fro…
Gilbert243 Oct 25, 2026
05d27ea
test(commerce): cover personal checkout permissions, idempotence and …
Gilbert243 Oct 25, 2026
82de37e
fix(mobile): reconcile owner-confirmed ticket and obligation payments…
Gilbert243 Oct 25, 2026
fde9e9d
feat(commerce): atomic direct Activity checkout without duplicate Jou…
Gilbert243 Oct 25, 2026
512270f
feat(discovery): expose verified native Commerce offers for public Ac…
Gilbert243 Oct 25, 2026
02b8a4c
feat(mobile): execute owner-backed direct Commerce actions with Occur…
Gilbert243 Oct 25, 2026
88f0d76
test(commerce): verify direct Activity checkout security and atomic i…
Gilbert243 Oct 25, 2026
5e83476
test(discovery): guard direct Commerce handoff and native routing
Gilbert243 Oct 25, 2026
32658b1
fix(commerce): confirm free purchase through owner services and const…
Gilbert243 Oct 25, 2026
d059b4d
fix(commerce): allow authenticated idempotent replay after free order…
Gilbert243 Oct 25, 2026
9733626
fix(obtention): expose personal active Journey and idempotent native …
Gilbert243 Oct 25, 2026
9bc4095
feat(funding): owner-checked contribution lookup for lost-response re…
Gilbert243 Oct 25, 2026
023ae92
fix(mobile): persist Commerce checkout idempotency across app restarts
Gilbert243 Oct 25, 2026
ecdf69b
fix(mobile): resume Funding contribution by owner reference after int…
Gilbert243 Oct 25, 2026
eb2aa51
fix(mobile): resume owner-confirmed Obtention Journeys after interrup…
Gilbert243 Oct 25, 2026
3f0d9b0
test(owners): verify Funding/Obtention recovery and cross-profile iso…
Gilbert243 Oct 25, 2026
3d66353
fix(commerce): use owner Offer availability property instead of calli…
Gilbert243 Oct 25, 2026
e79a316
fix(commerce): report existing orders before rejecting new checkout e…
Gilbert243 Oct 25, 2026
08d10fe
style(mobile): present owner-confirmed Commerce order states without …
Gilbert243 Oct 25, 2026
8ef1788
fix(commerce): reuse personal draft Journey for direct Activity checkout
Gilbert243 Oct 25, 2026
fd476d8
feat(journeys): expose native owner-verified invitation decisions
Gilbert243 Oct 25, 2026
71b8e90
fix(journeys): import canonical invitation statuses in projection
Gilbert243 Oct 25, 2026
2547de2
feat(mobile): accept or decline owner-backed Journey invitations nati…
Gilbert243 Oct 25, 2026
077c2a6
fix(mobile): preserve Service request idempotency and intent across a…
Gilbert243 Oct 25, 2026
4de939e
test(journeys): cover native invitation IDOR, idempotent decisions an…
Gilbert243 Oct 25, 2026
b4093f2
perf(discovery): preselect active Commerce offers before public hando…
Gilbert243 Oct 25, 2026
a7599de
fix(mobile): preserve submitted Commerce intent and require owner che…
Gilbert243 Oct 25, 2026
41269fd
fix(mobile): protect private Commerce routes and allow safe revision …
Gilbert243 Oct 25, 2026
4ae6a50
fix(mobile): resolve Dart compile errors in commerce_checkout_screen.…
Gilbert243 Oct 25, 2026
0efba11
fix(mobile): resolve Dart compile errors in obtention_choose_screen.dart
Gilbert243 Oct 25, 2026
d7533fb
fix(mobile): resolve Dart compile errors in service_start_screen.dart
Gilbert243 Oct 25, 2026
1c2e193
fix(mobile discover): remove practical-info wrapper and suppress unkn…
Gilbert243 Oct 25, 2026
0d5b2ed
refactor(mobile discover): remove obsolete N1 uncertainty annotation …
Gilbert243 Oct 25, 2026
4b0a759
test(mobile discover): omit unknown practical facts and empty N1 section
Gilbert243 Oct 25, 2026
e17e327
feat(discover): reuse owner-backed N1 composition for N2 depth
Gilbert243 Oct 25, 2026
b9b7cba
fix(discover): guest save intent never pretends an owner mutation suc…
Gilbert243 Oct 25, 2026
debbcd2
feat(discover): route N1 item depth to shared N2 and preserve owner c…
Gilbert243 Oct 25, 2026
a7807e0
feat(discover): render guest N2 with shared facts media and owner-act…
Gilbert243 Oct 25, 2026
975e309
fix(discover): keep guest N2 bound to its current public identity
Gilbert243 Oct 25, 2026
eca4c0d
fix(discover): reacquire N2 on route identity changes and bound wide …
Gilbert243 Oct 25, 2026
0c7ce99
test(discover): assert shared public N2 controls
Gilbert243 Oct 25, 2026
1d8beeb
test(discover): cover shared N2 media, owner routes and guest login i…
Gilbert243 Oct 25, 2026
35c3f33
feat(discover): show owner-known N2 occurrence dates without extra mo…
Gilbert243 Oct 25, 2026
a344ee3
fix(discover): revalidate N2 owner facts and permissions on entry
Gilbert243 Oct 25, 2026
6f706c8
style(discover): keep public N2 composition readable
Gilbert243 Oct 25, 2026
cd3071f
feat(discover): expose owner action continuation to guest N1 wrapper
Gilbert243 Oct 25, 2026
e5f9b1a
fix(discover): resume guest N1 native action after authentication
Gilbert243 Oct 25, 2026
675f3fc
test(discover): cover actual owner-projected N2 occurrence variants
Gilbert243 Oct 25, 2026
10fb157
fix(discover): use stable unique keys for each N2 date
Gilbert243 Oct 25, 2026
b0490c2
test(discover): assert N2 variant dates without duplicate-key assumption
Gilbert243 Oct 25, 2026
6aac4e1
test(discover): guest N2 preserves owner ticket continuation through …
Gilbert243 Oct 25, 2026
4aabb24
test(discover): expect shared summary and detail composition
Gilbert243 Oct 25, 2026
2ae8c7d
fix(discover): retain public browse-first owner destinations
Gilbert243 Oct 25, 2026
beb08c3
fix(discover): guest N1 opens public owner details without login
Gilbert243 Oct 25, 2026
582e355
fix(discover): guest N2 only authenticates when owner route requires …
Gilbert243 Oct 25, 2026
1d28212
test(discover): distinguish public owner browsing from protected cont…
Gilbert243 Oct 25, 2026
354ad9b
fix(discover): label UTC Occurrence instants faithfully in N2
Gilbert243 Oct 25, 2026
d0174cf
fix(discover): restore complete public owner-route predicate and vali…
Gilbert243 Oct 25, 2026
6c84a88
fix(discover): eliminate four duplicated Dart component copies causin…
Gilbert243 Oct 25, 2026
5b3210c
fix(discover): import shared public owner navigation in guest field
Gilbert243 Oct 25, 2026
c4319ff
fix(discover): close over validated N2 item in save callback
Gilbert243 Oct 25, 2026
dd6bd30
style(mobile): apply canonical Dart 3.13.3 formatter output from CI
Gilbert243 Oct 25, 2026
7abb8f4
fix(mobile): resolve Flutter analyzer diagnostics on owner continuati…
Gilbert243 Oct 25, 2026
497ebe2
style(mobile): apply canonical Dart format to funding labels
Gilbert243 Oct 25, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -244,8 +244,11 @@ jobs:
run: >-
python manage.py test
opportunities.tests
opportunities.test_public_source_knowledge
services.test_task32_opportunities
services.test_task32_concurrency
services.test_owner_relations
services.test_native_start_api
- name: Run Payments and Services T33 PostgreSQL tests
if: ${{ matrix.shard == 'commerce' }}
run: >-
Expand All @@ -262,6 +265,7 @@ jobs:
capacity.test_access_bridge
capacity.test_concurrency
commerce.tests
commerce.test_offer_price_facts
commerce.test_order_edge_cases
transport.tests
transport.test_task25_booking
Expand Down
17 changes: 16 additions & 1 deletion .github/workflows/mobile-android-build.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
name: Mobile Android Build
run-name: "Mobile DEV APK - ${{ github.head_ref || github.ref_name }}"

on:
pull_request:
Expand Down Expand Up @@ -58,11 +59,25 @@ jobs:
git diff --exit-code -- pubspec.lock
- name: Generate Dart sources
run: dart run build_runner build --delete-conflicting-outputs
- name: Validate remote DEV API
run: |
set -euo pipefail
jq -e '
.MAKOLO_ENVIRONMENT == "dev"
and (.MAKOLO_API_BASE_URL | type == "string"
and test("^https://[^/]+/?$"))
and (.MAKOLO_API_BASE_URL | test(
"^https://(localhost|127\\.0\\.0\\.1|10\\.|192\\.168\\.|172\\.(1[6-9]|2[0-9]|3[01])\\.|[^/]*\\.local)([:/]|$)";
"i"
) | not)
' config/dev.json > /dev/null
echo "DEV build configured for a remote HTTPS API."
- name: Build Android debug
run: flutter build apk --debug --flavor dev --dart-define-from-file=config/dev.json
- name: Upload DEV APK
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: makolo-dev-debug-apk
name: makolo-dev-debug-apk-${{ github.event.pull_request.head.sha || github.sha }}
path: mobile/build/app/outputs/flutter-apk/app-dev-debug.apk
if-no-files-found: error
retention-days: 14
47 changes: 47 additions & 0 deletions accounts/api/public_profile_views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
"""Minimal public Profile projection derived from the canonical Passeport.

This endpoint neither grants authority nor exposes the private Profile.
"""
from django.contrib.auth import get_user_model
from django.shortcuts import get_object_or_404
from rest_framework.permissions import AllowAny
from rest_framework.response import Response
from rest_framework.views import APIView

from core.api.projections import projection_envelope
from sharing.passport import PASSPORT_PUBLIC, build_profile_passport


class PublicProfileAPIView(APIView):
permission_classes = [AllowAny]

def get(self, request, profile_id):
user = get_object_or_404(
get_user_model().objects.filter(
is_active=True,
profile__public_profile=True,
profile__searchable=True,
),
pk=profile_id,
)
# The public Passeport remains the owner of this representation.
identity = build_profile_passport(
user, variant=PASSPORT_PUBLIC,
).identity
response = Response(projection_envelope(
projection="accounts.public-profile",
scope="public",
data={
"profile": {
"kind": "profile",
"id": str(user.pk),
"name": identity["name"],
"avatar_url": identity["avatar_url"],
"headline": identity["headline"],
"bio": identity["bio"],
"location": identity["location"],
},
},
))
response["Cache-Control"] = "public, max-age=60"
return response
2 changes: 2 additions & 0 deletions accounts/api/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from rest_framework.routers import DefaultRouter
from rest_framework_simplejwt.views import TokenRefreshView

from .public_profile_views import PublicProfileAPIView
from .views import (
AccountDeleteAPIView,
IdentifierAvailabilityAPIView,
Expand All @@ -25,6 +26,7 @@
router.register(r"users", UserViewSet, basename="users")

urlpatterns = [
path("public-profiles/<uuid:profile_id>/", PublicProfileAPIView.as_view(), name="public-profile"),
path("auth/register/", RegisterAPIView.as_view(), name="register"),
path(
"auth/identifier/availability/",
Expand Down
54 changes: 54 additions & 0 deletions accounts/test_public_profile_api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
from django.contrib.auth import get_user_model
from django.test import TestCase

from accounts.models import UserProfile


class PublicProfileAPITests(TestCase):
def setUp(self):
self.owner = get_user_model().objects.create_user(
username="native-public-owner",
first_name="Amina",
last_name="Test",
bio="Présentation publique",
phone="+243999000111",
email="private-profile@example.test",
)
self.profile = UserProfile.objects.create(
user=self.owner,
public_profile=True,
searchable=True,
city="Lubumbashi",
address="Ne jamais exposer",
)
self.url = f"/api/v1/accounts/public-profiles/{self.owner.pk}/"

def test_anonymous_sees_only_canonical_public_identity(self):
response = self.client.get(self.url)
self.assertEqual(response.status_code, 200)
profile = response.json()["data"]["profile"]
self.assertEqual(profile["kind"], "profile")
self.assertEqual(profile["id"], str(self.owner.pk))
self.assertEqual(profile["name"], "Amina Test")
self.assertEqual(profile["bio"], "Présentation publique")
self.assertNotIn("email", profile)
self.assertNotIn("phone", profile)
self.assertNotIn("address", profile)

def test_private_unsearchable_or_inactive_profiles_are_not_exposed(self):
for changes in (
{"public_profile": False},
{"searchable": False},
):
self.profile.public_profile = True
self.profile.searchable = True
for field, value in changes.items():
setattr(self.profile, field, value)
self.profile.save()
self.assertEqual(self.client.get(self.url).status_code, 404)
self.profile.public_profile = True
self.profile.searchable = True
self.profile.save()
self.owner.is_active = False
self.owner.save(update_fields=["is_active"])
self.assertEqual(self.client.get(self.url).status_code, 404)
204 changes: 204 additions & 0 deletions commerce/native_activity_checkout_api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
"""Activity-level Commerce checkout, without a universal Journey start."""
from django.core.exceptions import ValidationError as DomainValidationError
from django.db import transaction
from django.http import Http404
from django.shortcuts import get_object_or_404
from rest_framework import status
from rest_framework.exceptions import NotAuthenticated, ValidationError
from rest_framework.permissions import AllowAny
from rest_framework.response import Response
from rest_framework.views import APIView

from accounts.models import UserProfile
from activities.models import Activity, ActivityStatus, ActivityVisibility, Occurrence, OccurrenceStatus
from commerce.models import CommerceOrder, Offer, PaymentMode
from commerce.selectors import currently_available_offers
from commerce.services import create_order, confirm_order
from events.models import Event
from groups.selectors import eligible_activity_ids_for_profile
from journeys.models import Journey, JourneyStatus, WorkflowKind
from journeys.services import create_journey
from organizations.models import SpaceLifecycle
from payments.obligation_services import create_commerce_payment_obligation

from .native_checkout_api import ACTIVE_ORDER_STATUSES, CommerceCheckoutInput, _order_payload


def public_activity_for_commerce(actor, activity_id, *, lock=False):
qs = Activity.objects.filter(
pk=activity_id, status=ActivityStatus.PUBLISHED,
visibility=ActivityVisibility.PUBLIC,
).select_related("space", "owner_profile")
if lock:
qs = qs.select_for_update()
activity = get_object_or_404(qs)
if (
activity.pk not in eligible_activity_ids_for_profile(actor, [activity.pk])
or Event.objects.filter(activity=activity).exists()
):
raise Http404
if activity.space_id:
space = activity.space
visible = (
space.public_profile and space.searchable
and space.lifecycle == SpaceLifecycle.ACTIVE
and space.verification_status != "suspended"
)
else:
visible = UserProfile.objects.filter(
user_id=activity.owner_profile_id,
public_profile=True, searchable=True, user__is_active=True,
).exists()
if not visible:
raise Http404
return activity


def activity_offer_choices(activity):
"""Published Offer, Occurrence and Capacity facts; never infer from price."""
occurrences = list(
Occurrence.objects.filter(
activity=activity, status=OccurrenceStatus.SCHEDULED,
).order_by("start_at", "pk")[:50]
)
rows = []
for occurrence in occurrences or [None]:
for offer in currently_available_offers(
activity=activity, occurrence=occurrence,
).prefetch_related("payment_options"):
if not offer.is_currently_available:
continue
# after_approval requires a separately approved owner Journey.
modes = [mode for mode in offer.allowed_payment_modes
if mode != PaymentMode.AFTER_APPROVAL]
if not modes:
continue
rows.append({
"id": str(offer.pk),
"occurrence_id": str(occurrence.pk) if occurrence else None,
"occurrence_label": (
occurrence.label or str(occurrence.start_at or "")
if occurrence else ""
),
"label": offer.name,
"unit_price": str(offer.unit_price),
"currency": offer.currency,
"min_quantity": offer.min_quantity,
"max_quantity": offer.max_quantity,
"payment_modes": modes,
"default_payment_mode": offer.payment_mode if offer.payment_mode in modes else modes[0],
})
return rows


def active_activity_order(actor, activity):
if not getattr(actor, "is_authenticated", False):
return None
return CommerceOrder.objects.filter(
buyer=actor, journey__activity=activity,
status__in=ACTIVE_ORDER_STATUSES,
).order_by("-created_at", "-pk").first()


class ActivityCommerceCheckoutAPIView(APIView):
"""Atomically create a Purchase Journey and owner-confirmed CommerceOrder."""
permission_classes = [AllowAny]

def get(self, request, activity_id):
activity = public_activity_for_commerce(request.user, activity_id)
existing = active_activity_order(request.user, activity)
offers = activity_offer_choices(activity) if existing is None else []
response = Response({
"activity": {"id": str(activity.pk), "title": activity.title},
"journey_id": None,
"can_order": bool(offers),
"existing_order": _order_payload(existing) if existing else None,
"offers": offers,
})
response["Cache-Control"] = "private, no-store"
return response

@transaction.atomic
def post(self, request, activity_id):
if not getattr(request.user, "is_authenticated", False):
raise NotAuthenticated()
serializer = CommerceCheckoutInput(data=request.data)
serializer.is_valid(raise_exception=True)
data = serializer.validated_data
activity = public_activity_for_commerce(request.user, activity_id, lock=True)
occurrence_id = data.get("occurrence_id")
occurrence = (
get_object_or_404(Occurrence, pk=occurrence_id, activity=activity,
status=OccurrenceStatus.SCHEDULED)
if occurrence_id else None
)
key = data["idempotency_key"].strip()
prior = CommerceOrder.objects.filter(idempotency_key=key).first()
if prior is not None:
lines = list(prior.items.values("offer_id", "quantity"))
if (
prior.buyer_id != request.user.pk
or prior.journey.activity_id != activity.pk
or prior.journey.occurrence_id != (occurrence.pk if occurrence else None)
or prior.payment_mode != data["payment_mode"]
or len(lines) != 1
or lines[0]["offer_id"] != data["offer_id"]
or lines[0]["quantity"] != data["quantity"]
):
raise ValidationError({"idempotency_key": "Cette référence concerne une autre demande."})
result = Response(_order_payload(prior))
result["Cache-Control"] = "private, no-store"
return result
existing = active_activity_order(request.user, activity)
if existing is not None:
result = Response({
"detail": "Une commande existe déjà.",
"existing_order": _order_payload(existing),
}, status=status.HTTP_409_CONFLICT)
result["Cache-Control"] = "private, no-store"
return result
context_id = str(occurrence.pk) if occurrence else None
matching = next((
row for row in activity_offer_choices(activity)
if row["id"] == str(data["offer_id"])
and row["occurrence_id"] == context_id
and data["payment_mode"] in row["payment_modes"]
), None)
if matching is None:
raise ValidationError({"offer_id": "Cette offre n’est plus disponible."})
try:
# An unfinished personal purchase Journey is the same domain
# context, not a reason to create a duplicate one.
journey = (
Journey.objects.select_for_update(of=("self",))
.filter(
initiated_by=request.user, beneficiary=request.user,
activity=activity, occurrence=occurrence,
workflow=WorkflowKind.PURCHASE,
status=JourneyStatus.DRAFT,
)
.order_by("-created_at", "-pk")
.first()
)
if journey is None:
journey = create_journey(
initiated_by=request.user, beneficiary=request.user,
activity=activity, occurrence=occurrence,
workflow=WorkflowKind.PURCHASE,
)
order = create_order(
journey=journey, buyer=request.user,
selections=[{"offer": Offer.objects.get(pk=data["offer_id"]),
"quantity": data["quantity"]}],
payment_mode=data["payment_mode"],
idempotency_key=key,
)
if order.total > 0 and order.payment_mode == PaymentMode.UPFRONT:
create_commerce_payment_obligation(commerce_order=order, actor=request.user)
if order.total == 0 and order.payment_mode == PaymentMode.NONE:
confirm_order(order=order, actor=request.user)
except DomainValidationError as exc:
raise ValidationError({"detail": exc.messages}) from exc
response = Response(_order_payload(order), status=status.HTTP_201_CREATED)
response["Cache-Control"] = "private, no-store"
return response
11 changes: 11 additions & 0 deletions commerce/native_api_urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from django.urls import path

from .native_checkout_api import PersonalCommerceCheckoutAPIView
from .native_activity_checkout_api import ActivityCommerceCheckoutAPIView

app_name = "commerce_api"

urlpatterns = [
path("activities/<uuid:activity_id>/checkout/", ActivityCommerceCheckoutAPIView.as_view(), name="activity-checkout"),
path("journeys/<uuid:journey_id>/checkout/", PersonalCommerceCheckoutAPIView.as_view(), name="journey-checkout"),
]
Loading
Loading