diff --git a/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.DifferentiateWithoutColorInitialBottom_iPhone.png b/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.DifferentiateWithoutColorInitialBottom_iPhone.png index 06a9f76b9..8619751ad 100644 --- a/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.DifferentiateWithoutColorInitialBottom_iPhone.png +++ b/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.DifferentiateWithoutColorInitialBottom_iPhone.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d39ed2caa6762fe0942b037798074e4f9a53f1f52eb694633f4e82f1cc1f0210 -size 399590 +oid sha256:fd7b9da0fb992c7730eba5667063e74d55d273da28aaf4e3963477993741cd1f +size 1384837 diff --git a/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.DifferentiateWithoutColorInitialBottom_iPhone_dark.png b/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.DifferentiateWithoutColorInitialBottom_iPhone_dark.png index bd1b3b989..036c15b72 100644 --- a/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.DifferentiateWithoutColorInitialBottom_iPhone_dark.png +++ b/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.DifferentiateWithoutColorInitialBottom_iPhone_dark.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6b2eebf9ed7bce1ff183885b096021f4a66f5761964b30ddf3b404ffd2f18ab7 -size 374555 +oid sha256:f54009022eefc4d91491cba2f9a4c9cfea4c690ad164e296f021bb7c24ab67e0 +size 798090 diff --git a/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.DifferentiateWithoutColor_iPhone.png b/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.DifferentiateWithoutColor_iPhone.png index 305039a70..72bbdd9bc 100644 --- a/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.DifferentiateWithoutColor_iPhone.png +++ b/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.DifferentiateWithoutColor_iPhone.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:517afb07d387c965478225bd3683f53a54bc54822951f8da825bbbbb4e6b27b2 -size 346943 +oid sha256:247ae89fe129fc1c9d7b46b03a211cb83ffa370436388845ef64ca1024640ace +size 1406763 diff --git a/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.DifferentiateWithoutColor_iPhone_dark.png b/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.DifferentiateWithoutColor_iPhone_dark.png index 955f3863b..6396c258d 100644 --- a/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.DifferentiateWithoutColor_iPhone_dark.png +++ b/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.DifferentiateWithoutColor_iPhone_dark.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5d96311b839ae7c7dc5f4c2dac48a107548972fd098f47dcd3b295703173446a -size 348712 +oid sha256:3498a5f67d66b48d6bce2d1041faa0b2283908a240cf440b598fd44d0283cb5a +size 861573 diff --git a/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.InitialBottom_iPad.png b/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.InitialBottom_iPad.png index 7f55da383..31bde52a7 100644 --- a/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.InitialBottom_iPad.png +++ b/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.InitialBottom_iPad.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:20272851632b4dfaa4b5d11604028f6a3dfbe7a1526357fc130617951fe1345b -size 402883 +oid sha256:48f09d340616476f7d89b06b90f10148d600eab69196a9b001220ba1dfd54440 +size 2543177 diff --git a/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.InitialBottom_iPad_accessibility.png b/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.InitialBottom_iPad_accessibility.png index f02f22da9..da8819ebc 100644 --- a/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.InitialBottom_iPad_accessibility.png +++ b/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.InitialBottom_iPad_accessibility.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2bd088c1545bb52bdc03d7ca9d89c9bde5a78c830f3f2768213d43c97a1bd619 -size 562991 +oid sha256:6721345fd31a423e1bb1bc445676e98654eecf3826ee17d6bf735fcb23deec20 +size 2489637 diff --git a/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.InitialBottom_iPad_ax5.png b/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.InitialBottom_iPad_ax5.png index 3523875fe..1c468d6ff 100644 --- a/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.InitialBottom_iPad_ax5.png +++ b/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.InitialBottom_iPad_ax5.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6a6c5706ad0f1c1952e456c668dcf9f39a6ebaee0015d2bacd88eb652f74b4a5 -size 939746 +oid sha256:b63f40c719062eb87532e0e9d66e4c996b6f7adc3f6dadb16768a2f1dc00d84a +size 4807580 diff --git a/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.InitialBottom_iPad_contrast.png b/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.InitialBottom_iPad_contrast.png index 6c7dcc91d..10bec6141 100644 --- a/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.InitialBottom_iPad_contrast.png +++ b/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.InitialBottom_iPad_contrast.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1c5572f2ae67984cbf853ce869d88df5535786a6b9c20989d93aea6ad027d08a -size 406490 +oid sha256:7bd4705a23ae4abd762b304c9f85c3719b053518d0a080ecaa5950ae9c6c7775 +size 2574913 diff --git a/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.InitialBottom_iPad_dark.png b/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.InitialBottom_iPad_dark.png index bbb71c54d..f96768d36 100644 --- a/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.InitialBottom_iPad_dark.png +++ b/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.InitialBottom_iPad_dark.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:067cd0279c28d20cb9f2463ef1efe11d16bc6ba2fada5ea8c88cfb6df0176b27 -size 409025 +oid sha256:a931ad65526703cdc5ef297b635b099752a7ba75e6953fa71d29a2a09616bfbf +size 1501243 diff --git a/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.InitialBottom_iPhone.png b/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.InitialBottom_iPhone.png index 9dc44221b..a58266d38 100644 --- a/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.InitialBottom_iPhone.png +++ b/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.InitialBottom_iPhone.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4f631b9315c6dba94fabd35a1efe543bd0ccfd376e2e3bc435e9e0306064af21 -size 399905 +oid sha256:d0f3b22e2d4aa4d5649952dd0477b5d60282bfcee514e25310d6a2c66541b7c8 +size 1384692 diff --git a/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.InitialBottom_iPhone_accessibility.png b/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.InitialBottom_iPhone_accessibility.png index 75527e7c3..aee922348 100644 --- a/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.InitialBottom_iPhone_accessibility.png +++ b/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.InitialBottom_iPhone_accessibility.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:98b0cd455331a3f02076d9deb91c632ae8a781b0772171738bef0884568fed7f -size 417005 +oid sha256:26c38a62ed489965a2293401536336b0dbf530f05fc879efc52ce9cd6bc73d6e +size 1390052 diff --git a/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.InitialBottom_iPhone_ax5.png b/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.InitialBottom_iPhone_ax5.png index c1ad4f0b8..d60256d24 100644 --- a/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.InitialBottom_iPhone_ax5.png +++ b/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.InitialBottom_iPhone_ax5.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:88e8afa38895b9c45fee312ba62ac94c678e3c0c9a25cbdef0814f57dec209cf -size 551604 +oid sha256:78db2fb145e8b1f8e6eede3aff92e464dfbf0980579558df6b7f4947fb0bf85f +size 2236104 diff --git a/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.InitialBottom_iPhone_contrast.png b/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.InitialBottom_iPhone_contrast.png index 18b970896..8ece7c4e2 100644 --- a/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.InitialBottom_iPhone_contrast.png +++ b/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.InitialBottom_iPhone_contrast.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dce28bee69c834257064c4261eb41c92572f45d5c2fe7a61579c15436b8249e5 -size 413613 +oid sha256:6fdcff2d2464debadf896845101014e4e13d4cc99a437454940542ee82f45a1e +size 1375655 diff --git a/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.InitialBottom_iPhone_dark.png b/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.InitialBottom_iPhone_dark.png index cc9e668e5..5f6912681 100644 --- a/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.InitialBottom_iPhone_dark.png +++ b/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.InitialBottom_iPhone_dark.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c0f6663a48f7672cdbc03d293383a4f51ec2da38cdc3ccd9f775af175e9f1c72 -size 373395 +oid sha256:fa46c564ab87a53ad2441bd69ebaf6d9d2114ae235806e358d9d7253a979e972 +size 793500 diff --git a/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.PlannedStayAfterRecordingGap_iPhone.png b/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.PlannedStayAfterRecordingGap_iPhone.png new file mode 100644 index 000000000..ade00f1fd --- /dev/null +++ b/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.PlannedStayAfterRecordingGap_iPhone.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2493064b98771b3086d329c329c1042f57711dfeb163a2aa632010d53e31a79f +size 1343431 diff --git a/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.PlannedStayAfterRecordingGap_iPhone_dark.png b/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.PlannedStayAfterRecordingGap_iPhone_dark.png new file mode 100644 index 000000000..2f8fadbcc --- /dev/null +++ b/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.PlannedStayAfterRecordingGap_iPhone_dark.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3df384db63f680c999c99e8bf38a25e1db59867adc8d090e5f66d50767708135 +size 764302 diff --git a/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.PlannedStayDifferentRegion_iPhone.png b/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.PlannedStayDifferentRegion_iPhone.png new file mode 100644 index 000000000..bda28e4ec --- /dev/null +++ b/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.PlannedStayDifferentRegion_iPhone.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2b1501b03e054e05936523a93ec99aeefcbb1a29e3fb3d9ab54b5d6cd4be370b +size 1344751 diff --git a/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.PlannedStayDifferentRegion_iPhone_dark.png b/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.PlannedStayDifferentRegion_iPhone_dark.png new file mode 100644 index 000000000..754ba10f1 --- /dev/null +++ b/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.PlannedStayDifferentRegion_iPhone_dark.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5d2651bc1358dc5ea353f3b69bd315c5b3d081e41e4338188f676d15283c9f76 +size 765255 diff --git a/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.PlannedStay_iPhone.png b/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.PlannedStay_iPhone.png index dcf23a1aa..585df8afb 100644 --- a/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.PlannedStay_iPhone.png +++ b/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.PlannedStay_iPhone.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:de920a261347f2cdbb47eed2de584583dfcd27e01ccdc6d01cf3074c02e6372f -size 218548 +oid sha256:fec43f012b275635fc4f26c8d3c907a2e33f26b4ce094797c5683315e1c1d08b +size 1329722 diff --git a/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.PlannedStay_iPhone_accessibility.png b/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.PlannedStay_iPhone_accessibility.png index a1d8480bf..b79d1a5a0 100644 --- a/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.PlannedStay_iPhone_accessibility.png +++ b/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.PlannedStay_iPhone_accessibility.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e7c3f596af11216a4814a07498157b33d1bafa2efcb912c8aaa9c13e66aaf643 -size 332590 +oid sha256:63befcc07248e9cbe21ff14a6f7c9faf46caaf45e65524c2d391d43fdb656792 +size 1494377 diff --git a/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.PlannedStay_iPhone_dark.png b/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.PlannedStay_iPhone_dark.png index 2af91afee..4be1913da 100644 --- a/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.PlannedStay_iPhone_dark.png +++ b/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.PlannedStay_iPhone_dark.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f68728fcf011d370c27443cbe735251aed48ceab10fd13e57a450cb49d4a447f -size 224959 +oid sha256:afc558716f8618dd5164fc32a696a72a09ee397f7a08787f2de1cb21ad29bf0e +size 745801 diff --git a/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.ShortPlannedStay_iPhone.png b/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.ShortPlannedStay_iPhone.png new file mode 100644 index 000000000..a5bd0fe82 --- /dev/null +++ b/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.ShortPlannedStay_iPhone.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a6c4460a692e09fae7b44029c68547e2041bac6d93299509e63ea91881c1afbe +size 1323628 diff --git a/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.ShortPlannedStay_iPhone_dark.png b/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.ShortPlannedStay_iPhone_dark.png new file mode 100644 index 000000000..21621db35 --- /dev/null +++ b/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.ShortPlannedStay_iPhone_dark.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e71c00bc0004317a6f00973bd012766fad1f077ded071d98e18b3e459f2e4e5f +size 739856 diff --git a/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.WithData_iPad.png b/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.WithData_iPad.png index 7f55da383..05703f4fb 100644 --- a/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.WithData_iPad.png +++ b/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.WithData_iPad.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:20272851632b4dfaa4b5d11604028f6a3dfbe7a1526357fc130617951fe1345b -size 402883 +oid sha256:e504eaba70b73cbd04eb7aad5579b8f9c44fe4b7b0461737479e0f068fd4912f +size 2420814 diff --git a/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.WithData_iPad_accessibility.png b/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.WithData_iPad_accessibility.png index f02f22da9..1be4bc317 100644 --- a/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.WithData_iPad_accessibility.png +++ b/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.WithData_iPad_accessibility.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2bd088c1545bb52bdc03d7ca9d89c9bde5a78c830f3f2768213d43c97a1bd619 -size 562991 +oid sha256:40189401720d51588cdc29c64443fd08e260810b19f220012fdfcca5d6d15528 +size 2499854 diff --git a/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.WithData_iPad_ax5.png b/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.WithData_iPad_ax5.png index 63a16c412..38ce8e5e1 100644 --- a/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.WithData_iPad_ax5.png +++ b/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.WithData_iPad_ax5.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0d18e4cae3737e79181d430acab79602714cab862cfcae24774e13df0c51c83a -size 650767 +oid sha256:c2b95cb7ea18e8a57617884c953cd44c4e71f00d65c2d03515e9e9baed2df5b5 +size 5012922 diff --git a/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.WithData_iPad_contrast.png b/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.WithData_iPad_contrast.png index 6c7dcc91d..207daa03f 100644 --- a/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.WithData_iPad_contrast.png +++ b/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.WithData_iPad_contrast.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1c5572f2ae67984cbf853ce869d88df5535786a6b9c20989d93aea6ad027d08a -size 406490 +oid sha256:3b7e1b237dcc857d65a2b935e50717cce418cf226cb8aa5fa0ea91aeb826197a +size 2434370 diff --git a/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.WithData_iPad_dark.png b/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.WithData_iPad_dark.png index bbb71c54d..4fb7f197b 100644 --- a/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.WithData_iPad_dark.png +++ b/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.WithData_iPad_dark.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:067cd0279c28d20cb9f2463ef1efe11d16bc6ba2fada5ea8c88cfb6df0176b27 -size 409025 +oid sha256:1855d1e49337483170ed6b1dcec03d3983b9834815183f573b423af0ae7d9476 +size 1405924 diff --git a/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.WithData_iPhone.png b/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.WithData_iPhone.png index 07a82dee1..40e9a9215 100644 --- a/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.WithData_iPhone.png +++ b/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.WithData_iPhone.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5034d3b60d0e641df3da6db6f6a6961cf3a465f5b5d764d65ef343e04a959c31 -size 279459 +oid sha256:ccb1f03fc4ce00e97f0d4c1769412df30322817118dde1ea12f9fb5f935039d6 +size 1340753 diff --git a/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.WithData_iPhone_accessibility.png b/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.WithData_iPhone_accessibility.png index 8d8c2c9fc..ec255a0c2 100644 --- a/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.WithData_iPhone_accessibility.png +++ b/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.WithData_iPhone_accessibility.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2ae20b26fe04dea8a1285e2ec3b80f14efea6ce3fd65fd1cc880d0fd82c2f691 -size 420968 +oid sha256:4c6aa4672bfc2610650d5a69881a75c9904b240b411d40efd780f472dc1f6e3d +size 1508959 diff --git a/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.WithData_iPhone_ax5.png b/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.WithData_iPhone_ax5.png index 3776ace08..0d0c88830 100644 --- a/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.WithData_iPhone_ax5.png +++ b/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.WithData_iPhone_ax5.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:95282e816c5b79ef0bffdf2e9feba50bf29ffadf014013bcb25884b082f72819 -size 565666 +oid sha256:dbff7c2bb9e2b59294bcbc95ba6c66944174b195bf05daaf41cbfc3dadbcf75b +size 3309171 diff --git a/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.WithData_iPhone_contrast.png b/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.WithData_iPhone_contrast.png index fda3ddd77..a723c72b2 100644 --- a/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.WithData_iPhone_contrast.png +++ b/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.WithData_iPhone_contrast.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3d4e09a622c766f7bc0de77cdf118374c405dbd9a74978ad626a73691895c285 -size 284158 +oid sha256:9544d375d67c9dd6f4701a4fdaa45450e82d12aea963b3665317fb383dd77847 +size 1350163 diff --git a/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.WithData_iPhone_dark.png b/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.WithData_iPhone_dark.png index 17e98a193..4eb0e8ef6 100644 --- a/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.WithData_iPhone_dark.png +++ b/Where/WhereUI/SnapshotTests/__Snapshots__/PresenceTimelineListSnapshotTests/presenceTimeline.WithData_iPhone_dark.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:88e2657da02f2f47465d3abe71c61f1d096a9a3817fae750ab7eb32ade302019 -size 282703 +oid sha256:b10f1d8f07fc60b3f6f106d7e418b11bc6ebf494d7c9260be60d0faa69fdcebe +size 796709 diff --git a/Where/WhereUI/Sources/Preview/PreviewSupport.swift b/Where/WhereUI/Sources/Preview/PreviewSupport.swift index cf56ec64c..8c56af8de 100644 --- a/Where/WhereUI/Sources/Preview/PreviewSupport.swift +++ b/Where/WhereUI/Sources/Preview/PreviewSupport.swift @@ -341,12 +341,15 @@ @MainActor public static func plannedStayYearReportModel( showsEstimatedTimeAndPlanning: Bool = true, + plannedRegion: Region = .newYork, + recordedThroughDay: CalendarDay? = nil, + plannedThroughDay: CalendarDay = CalendarDay(year: year, month: 8, day: 15), ) -> YearReportModel { let completeReport = sampleReport() var calendar = Calendar(identifier: .gregorian) calendar.timeZone = TimeZone(identifier: "America/Los_Angeles")! let today = CalendarDay(from: referenceNow, in: calendar) - let recordedDays = completeReport.days.filter { $0.day <= today } + let recordedDays = completeReport.days.filter { $0.day <= recordedThroughDay ?? today } var recordedTotals: [Region: Int] = [:] for day in recordedDays { for region in day.regions { @@ -370,8 +373,8 @@ now: { referenceNow }, ) model.forecasts.setActivePlannedStay(PlannedStay( - region: .newYork, - through: CalendarDay(year: year, month: 8, day: 15), + region: plannedRegion, + through: plannedThroughDay, )) return model } diff --git a/Where/WhereUI/Sources/Primary/PlannedPresenceJourneyBackground.swift b/Where/WhereUI/Sources/Primary/PlannedPresenceJourneyBackground.swift new file mode 100644 index 000000000..d8dab8e4a --- /dev/null +++ b/Where/WhereUI/Sources/Primary/PlannedPresenceJourneyBackground.swift @@ -0,0 +1,71 @@ +import SwiftUI + +/// Draws the lighter planned treatment, fading it in after a joined recorded card. +struct PlannedPresenceJourneyBackground: View { + let position: PresenceJourneyCardPosition + let tint: Color + + @Environment(\.stylesheet) private var stylesheet + + var body: some View { + let timeline = stylesheet.timeline + let row = timeline.row + let planned = timeline.planned + let shape = position.shape(cornerRadius: row.cornerRadius) + + ZStack { + if position.isJoinedContinuation { + VStack(spacing: 0) { + LinearGradient( + colors: [ + tint.opacity(row.fillOpacity), + tint.opacity(planned.fillOpacity), + ], + startPoint: .top, + endPoint: .bottom, + ) + .frame(height: planned.transitionHeight) + + tint.opacity(planned.fillOpacity) + } + } else { + shape + .fill(tint.opacity(planned.fillOpacity)) + } + + PlannedStayHatch( + color: tint, + spacing: planned.hatchSpacing, + lineWidth: planned.hatchLineWidth, + gridOriginX: 0, + ) + .opacity(planned.hatchOpacity) + .mask { + if position.isJoinedContinuation { + VStack(spacing: 0) { + LinearGradient( + colors: [.clear, .white], + startPoint: .top, + endPoint: .bottom, + ) + .frame(height: planned.transitionHeight) + + Color.white + } + } else { + Color.white + } + } + } + .clipShape(shape) + } +} + +#if DEBUG + #Preview { + PlannedPresenceJourneyBackground(position: .bottom, tint: .blue) + .frame(width: 240, height: 120) + .padding() + .whereBroadwayRoot() + } +#endif diff --git a/Where/WhereUI/Sources/Primary/PlannedPresenceJourneyCardContent.swift b/Where/WhereUI/Sources/Primary/PlannedPresenceJourneyCardContent.swift new file mode 100644 index 000000000..429cfd6e5 --- /dev/null +++ b/Where/WhereUI/Sources/Primary/PlannedPresenceJourneyCardContent.swift @@ -0,0 +1,119 @@ +import SwiftUI + +/// Lays out a planned stay as a full row or a compact joined continuation. +struct PlannedPresenceJourneyCardContent: View { + let regionName: String + let dateRange: String + let dayCount: Int + let daysInYear: Int + let position: PresenceJourneyCardPosition + let tint: Color + + @Environment(\.stylesheet) private var stylesheet + + var body: some View { + let timeline = stylesheet.timeline + let row = timeline.row + let planned = timeline.planned + let countLayout = row.stacksDayCount + ? AnyLayout(VStackLayout(alignment: .leading, spacing: row.labelSpacing)) + : AnyLayout(HStackLayout(alignment: .center, spacing: row.spacing)) + let baseHeight = position.isJoinedContinuation + ? planned.joinedBaseHeight + : row.baseHeight + let proportionalHeight = baseHeight + + row.yearScaleHeight * CGFloat(dayCount) / CGFloat(daysInYear) + + countLayout { + if position.isJoinedContinuation { + if row.stacksDayCount { + VStack(alignment: .leading, spacing: row.labelSpacing) { + Text(regionName) + .font(.subheadline.bold()) + Text(dateRange) + .font(.subheadline) + .foregroundStyle(.secondary) + } + .frame(maxWidth: .infinity, alignment: .leading) + } else { + HStack(spacing: planned.joinedLabelSpacing) { + Text(regionName) + .font(.subheadline.bold()) + .layoutPriority(1) + Text(dateRange) + .font(.subheadline) + .foregroundStyle(.secondary) + .lineLimit(1) + } + .frame(maxWidth: .infinity, alignment: .leading) + } + + Text(WhereFormat.dayCount(dayCount)) + .font(.caption.bold()) + .monospacedDigit() + .padding(.horizontal, planned.joinedCountHorizontalPadding) + .padding(.vertical, planned.joinedCountVerticalPadding) + .background { + Capsule() + .fill(tint.opacity(row.countFillOpacity / 2)) + } + } else { + VStack(alignment: .leading, spacing: row.labelSpacing) { + Text(regionName) + .font(.headline) + Text(dateRange) + .font(.subheadline) + .foregroundStyle(.secondary) + Text(String(localized: .timelinePlannedStay)) + .font(.caption) + .foregroundStyle(.secondary) + .opacity(planned.labelOpacity) + } + .frame(maxWidth: .infinity, alignment: .leading) + + Text(WhereFormat.dayCount(dayCount)) + .font(.subheadline.bold()) + .monospacedDigit() + .padding(.horizontal, row.countHorizontalPadding) + .padding(.vertical, row.countVerticalPadding) + .background { + Capsule() + .fill(tint.opacity(row.countFillOpacity / 2)) + } + } + } + .frame(minHeight: proportionalHeight) + .padding(.horizontal, row.horizontalPadding) + .padding( + .vertical, + position.isJoinedContinuation + ? planned.joinedVerticalPadding + : row.verticalPadding, + ) + } +} + +#if DEBUG + #Preview { + VStack(spacing: 0) { + PlannedPresenceJourneyCardContent( + regionName: "New York", + dateRange: "Jul 16 – Aug 15", + dayCount: 31, + daysInYear: 365, + position: .bottom, + tint: .blue, + ) + PlannedPresenceJourneyCardContent( + regionName: "New York", + dateRange: "Jul 16 – Aug 15", + dayCount: 31, + daysInYear: 365, + position: .standalone, + tint: .blue, + ) + } + .padding() + .whereBroadwayRoot() + } +#endif diff --git a/Where/WhereUI/Sources/Primary/PlannedPresenceJourneyRow.swift b/Where/WhereUI/Sources/Primary/PlannedPresenceJourneyRow.swift index af94573c7..1e8424357 100644 --- a/Where/WhereUI/Sources/Primary/PlannedPresenceJourneyRow.swift +++ b/Where/WhereUI/Sources/Primary/PlannedPresenceJourneyRow.swift @@ -8,6 +8,7 @@ struct PlannedPresenceJourneyRow: View { let calendar: Calendar let daysInYear: Int let isFirst: Bool + let cardPosition: PresenceJourneyCardPosition @Environment(\.stylesheet) private var stylesheet @Environment(\.regionStyles) private var regionStyles @@ -25,66 +26,34 @@ struct PlannedPresenceJourneyRow: View { end: end, calendar: calendar, ) - let countLayout = row.stacksDayCount - ? AnyLayout(VStackLayout(alignment: .leading, spacing: row.labelSpacing)) - : AnyLayout(HStackLayout(alignment: .center, spacing: row.spacing)) - let proportionalHeight = row.baseHeight - + row.yearScaleHeight * CGFloat(interval.dayCount) / CGFloat(daysInYear) - let cardShape = RoundedRectangle(cornerRadius: row.cornerRadius) - HStack(spacing: rail.toCardSpacing) { Color.clear .frame(width: rail.nodeSize) - countLayout { - VStack(alignment: .leading, spacing: row.labelSpacing) { - Text(interval.region.localizedName) - .font(.headline) - Text(dateRange) - .font(.subheadline) - .foregroundStyle(.secondary) - Text(String(localized: .timelinePlannedStay)) - .font(.caption) - .foregroundStyle(.secondary) - .opacity(planned.labelOpacity) - } - .frame(maxWidth: .infinity, alignment: .leading) - - Text(WhereFormat.dayCount(interval.dayCount)) - .font(.subheadline.bold()) - .monospacedDigit() - .padding(.horizontal, row.countHorizontalPadding) - .padding(.vertical, row.countVerticalPadding) - .background { - Capsule() - .fill(style.tint.opacity(row.countFillOpacity / 2)) - } - } - .frame(minHeight: proportionalHeight) - .padding(.horizontal, row.horizontalPadding) - .padding(.vertical, row.verticalPadding) + PlannedPresenceJourneyCardContent( + regionName: interval.region.localizedName, + dateRange: dateRange, + dayCount: interval.dayCount, + daysInYear: daysInYear, + position: cardPosition, + tint: style.tint, + ) .background { - ZStack { - cardShape - .fill(style.tint.opacity(planned.fillOpacity)) - PlannedStayHatch( - color: style.tint, - spacing: planned.hatchSpacing, - lineWidth: planned.hatchLineWidth, - gridOriginX: 0, - ) - .opacity(planned.hatchOpacity) - .clipShape(cardShape) - } + PlannedPresenceJourneyBackground( + position: cardPosition, + tint: style.tint, + ) } .overlay { - cardShape.stroke( - style.tint.opacity(planned.borderOpacity), + PresenceJourneyCardBorder( + position: cardPosition, + cornerRadius: row.cornerRadius, + color: style.tint.opacity(planned.borderOpacity), lineWidth: row.borderWidth, ) } } - .padding(.vertical, row.gap / 2) + .padding(cardPosition.gapEdges, row.gap / 2) .background(alignment: .leading) { PresenceJourneyRail( tint: style.tint.opacity(planned.labelOpacity), @@ -106,16 +75,30 @@ struct PlannedPresenceJourneyRow: View { #if DEBUG #Preview { - PlannedPresenceJourneyRow( - interval: LocationForecastModel.PlannedInterval( - region: .newYork, - start: CalendarDay(year: 2026, month: 7, day: 16), - end: CalendarDay(year: 2026, month: 8, day: 15), - ), - calendar: Calendar(identifier: .gregorian), - daysInYear: 365, - isFirst: false, - ) + VStack(spacing: 0) { + PlannedPresenceJourneyRow( + interval: LocationForecastModel.PlannedInterval( + region: .newYork, + start: CalendarDay(year: 2026, month: 7, day: 16), + end: CalendarDay(year: 2026, month: 8, day: 15), + ), + calendar: Calendar(identifier: .gregorian), + daysInYear: 365, + isFirst: false, + cardPosition: .standalone, + ) + PlannedPresenceJourneyRow( + interval: LocationForecastModel.PlannedInterval( + region: .newYork, + start: CalendarDay(year: 2026, month: 7, day: 16), + end: CalendarDay(year: 2026, month: 7, day: 24), + ), + calendar: Calendar(identifier: .gregorian), + daysInYear: 365, + isFirst: false, + cardPosition: .bottom, + ) + } .padding() .whereBroadwayRoot() } diff --git a/Where/WhereUI/Sources/Primary/PresenceJourneyCardBorder.swift b/Where/WhereUI/Sources/Primary/PresenceJourneyCardBorder.swift new file mode 100644 index 000000000..0dc00449a --- /dev/null +++ b/Where/WhereUI/Sources/Primary/PresenceJourneyCardBorder.swift @@ -0,0 +1,37 @@ +import SwiftUI + +/// Draws only the outer stroke of a standalone or joined journey-card segment. +struct PresenceJourneyCardBorder: View { + let position: PresenceJourneyCardPosition + let cornerRadius: CGFloat + let color: Color + let lineWidth: CGFloat + + var body: some View { + switch position { + case .standalone: + position.shape(cornerRadius: cornerRadius) + .stroke(color, lineWidth: lineWidth) + case .top, .bottom: + position.shape(cornerRadius: cornerRadius) + .stroke(color, lineWidth: lineWidth) + .mask { + Rectangle() + .padding(position.joinedEdge, lineWidth) + } + } + } +} + +#if DEBUG + #Preview { + PresenceJourneyCardBorder( + position: .standalone, + cornerRadius: 18, + color: .blue, + lineWidth: 1, + ) + .frame(width: 240, height: 120) + .padding() + } +#endif diff --git a/Where/WhereUI/Sources/Primary/PresenceJourneyCardPosition.swift b/Where/WhereUI/Sources/Primary/PresenceJourneyCardPosition.swift new file mode 100644 index 000000000..886416397 --- /dev/null +++ b/Where/WhereUI/Sources/Primary/PresenceJourneyCardPosition.swift @@ -0,0 +1,66 @@ +import SwiftUI + +/// Which outer corners and list gaps a journey-card segment owns. +enum PresenceJourneyCardPosition { + case standalone + case top + case bottom + + func shape(cornerRadius: CGFloat) -> UnevenRoundedRectangle { + UnevenRoundedRectangle( + topLeadingRadius: roundsTop ? cornerRadius : 0, + bottomLeadingRadius: roundsBottom ? cornerRadius : 0, + bottomTrailingRadius: roundsBottom ? cornerRadius : 0, + topTrailingRadius: roundsTop ? cornerRadius : 0, + ) + } + + var gapEdges: Edge.Set { + switch self { + case .standalone: + [.top, .bottom] + case .top: + .top + case .bottom: + .bottom + } + } + + var joinedEdge: Edge.Set { + switch self { + case .standalone: + [] + case .top: + .bottom + case .bottom: + .top + } + } + + var isJoinedContinuation: Bool { + switch self { + case .standalone, .top: + false + case .bottom: + true + } + } + + private var roundsTop: Bool { + switch self { + case .standalone, .top: + true + case .bottom: + false + } + } + + private var roundsBottom: Bool { + switch self { + case .standalone, .bottom: + true + case .top: + false + } + } +} diff --git a/Where/WhereUI/Sources/Primary/PresenceJourneyRow.swift b/Where/WhereUI/Sources/Primary/PresenceJourneyRow.swift index 7d73a8ae9..1d8c2b6bf 100644 --- a/Where/WhereUI/Sources/Primary/PresenceJourneyRow.swift +++ b/Where/WhereUI/Sources/Primary/PresenceJourneyRow.swift @@ -8,6 +8,7 @@ struct PresenceJourneyRow: View { let daysInYear: Int let isFirst: Bool let isLast: Bool + let cardPosition: PresenceJourneyCardPosition @Environment(\.stylesheet) private var stylesheet @Environment(\.regionStyles) private var regionStyles @@ -27,6 +28,7 @@ struct PresenceJourneyRow: View { : AnyLayout(HStackLayout(alignment: .center, spacing: row.spacing)) let proportionalHeight = row.baseHeight + row.yearScaleHeight * CGFloat(stint.dayCount) / CGFloat(daysInYear) + let cardShape = cardPosition.shape(cornerRadius: row.cornerRadius) HStack(spacing: rail.toCardSpacing) { Color.clear @@ -56,18 +58,18 @@ struct PresenceJourneyRow: View { .padding(.horizontal, row.horizontalPadding) .padding(.vertical, row.verticalPadding) .background { - RoundedRectangle(cornerRadius: row.cornerRadius) - .fill(style.tint.opacity(row.fillOpacity)) + cardShape.fill(style.tint.opacity(row.fillOpacity)) } .overlay { - RoundedRectangle(cornerRadius: row.cornerRadius) - .stroke( - style.tint.opacity(row.borderOpacity), - lineWidth: row.borderWidth, - ) + PresenceJourneyCardBorder( + position: cardPosition, + cornerRadius: row.cornerRadius, + color: style.tint.opacity(row.borderOpacity), + lineWidth: row.borderWidth, + ) } } - .padding(.vertical, row.gap / 2) + .padding(cardPosition.gapEdges, row.gap / 2) .background(alignment: .leading) { PresenceJourneyRail( tint: style.tint, @@ -99,6 +101,7 @@ struct PresenceJourneyRow: View { daysInYear: report.daysInSelectedYear, isFirst: true, isLast: false, + cardPosition: .standalone, ) .padding() .whereBroadwayRoot() diff --git a/Where/WhereUI/Sources/Primary/PresenceTimelineList.swift b/Where/WhereUI/Sources/Primary/PresenceTimelineList.swift index 69aeaaad4..d2941c584 100644 --- a/Where/WhereUI/Sources/Primary/PresenceTimelineList.swift +++ b/Where/WhereUI/Sources/Primary/PresenceTimelineList.swift @@ -1,3 +1,4 @@ +import RegionKit import SFSafeSymbols import SnapshotKit import SwiftUI @@ -15,6 +16,15 @@ struct PresenceTimelineList: View { let report: YearReportModel @Environment(\.stylesheet) private var stylesheet + @State private var plannedStayEditorTarget: PlannedStayEditorTarget? + + private struct PlannedStayEditorTarget: Identifiable { + let region: Region + + var id: Region { + region + } + } var body: some View { let yearReport = report.report @@ -22,72 +32,117 @@ struct PresenceTimelineList: View { let plannedInterval = report.showsEstimatedTimeAndPlanning ? report.forecasts.plannedInterval(intersecting: report.selectedYear) : nil - - if stints.isEmpty, plannedInterval == nil { - ContentUnavailableView { - Label( - String(localized: .timelineEmptyTitle), - systemSymbol: .calendarDayTimelineLeft, - ) - } description: { - Text(String(localized: .timelineEmptyDescription)) - } + let joinsPlannedStay = if let plannedInterval, let currentStint = stints.last { + plannedInterval.region == currentStint.region + && CalendarDay(from: currentStint.end, in: report.calendar).adding(days: 1) + == plannedInterval.start } else { - let pinsOverview = stylesheet.timeline.overview.pinsToViewport + false + } - ScrollView { - LazyVStack(spacing: pinsOverview ? 0 : stylesheet.spacing.large) { - if !pinsOverview { - YearRibbon( - days: yearReport?.days ?? [], - year: report.selectedYear, - calendar: report.calendar, - ) - } + Group { + if stints.isEmpty, plannedInterval == nil { + ContentUnavailableView { + Label( + String(localized: .timelineEmptyTitle), + systemSymbol: .calendarDayTimelineLeft, + ) + } description: { + Text(String(localized: .timelineEmptyDescription)) + } + } else { + let pinsOverview = stylesheet.timeline.overview.pinsToViewport - LazyVStack(spacing: 0) { - ForEach(stints.enumerated(), id: \.element.id) { index, stint in - PresenceJourneyRow( - stint: stint, + ScrollView { + LazyVStack(spacing: stylesheet.spacing.large) { + if !pinsOverview { + YearRibbon( + days: yearReport?.days ?? [], + year: report.selectedYear, calendar: report.calendar, - daysInYear: report.daysInSelectedYear, - isFirst: index == stints.startIndex, - isLast: plannedInterval == nil - && index == stints.index(before: stints.endIndex), ) } - if let plannedInterval { - PlannedPresenceJourneyRow( - interval: plannedInterval, - calendar: report.calendar, - daysInYear: report.daysInSelectedYear, - isFirst: stints.isEmpty, + LazyVStack(spacing: 0) { + ForEach(stints.enumerated(), id: \.element.id) { index, stint in + PresenceJourneyRow( + stint: stint, + calendar: report.calendar, + daysInYear: report.daysInSelectedYear, + isFirst: index == stints.startIndex, + isLast: plannedInterval == nil + && index == stints.index(before: stints.endIndex), + cardPosition: joinsPlannedStay + && index == stints.index(before: stints.endIndex) + ? .top + : .standalone, + ) + } + + if let plannedInterval { + PlannedPresenceJourneyRow( + interval: plannedInterval, + calendar: report.calendar, + daysInYear: report.daysInSelectedYear, + isFirst: stints.isEmpty, + cardPosition: joinsPlannedStay ? .bottom : .standalone, + ) + } + } + + if showsForecast { + LocationForecastPanel( + forecasts: timelineForecasts, + microprintRegions: report.ranking.primary.map(\.region), + plannedStay: report.forecasts.activePlannedStay, + editableRegions: report.ranking.primary.map(\.region), + editAction: { region in + plannedStayEditorTarget = + PlannedStayEditorTarget(region: region) + }, + clearAction: report.forecasts.clear, ) } } - } - .padding(.horizontal, stylesheet.spacing.xxLarge) - .padding(.top, pinsOverview ? 0 : stylesheet.spacing.large) - .padding(.bottom, stylesheet.spacing.large) - } - .safeAreaInset( - edge: .top, - spacing: 0, - ) { - if pinsOverview { - YearRibbon( - days: yearReport?.days ?? [], - year: report.selectedYear, - calendar: report.calendar, - ) .padding(.horizontal, stylesheet.spacing.xxLarge) - .padding(.top, stylesheet.spacing.large) + .padding(.top, pinsOverview ? 0 : stylesheet.spacing.large) .padding(.bottom, stylesheet.spacing.large) } + .safeAreaInset( + edge: .top, + spacing: 0, + ) { + if pinsOverview { + YearRibbon( + days: yearReport?.days ?? [], + year: report.selectedYear, + calendar: report.calendar, + ) + .padding(.horizontal, stylesheet.spacing.xxLarge) + .padding(.top, stylesheet.spacing.large) + .padding(.bottom, stylesheet.spacing.large) + } + } + .defaultScrollAnchor(.bottom, for: .initialOffset) + .id(report.selectedYear) } - .defaultScrollAnchor(.bottom, for: .initialOffset) - .id(report.selectedYear) + } + .sheet(item: $plannedStayEditorTarget) { target in + PlannedStayEditor( + region: target.region, + model: report.forecasts, + driftThreshold: report.driftThreshold, + ) + } + } + + private var showsForecast: Bool { + report.showsEstimatedTimeAndPlanning && !timelineForecasts.isEmpty + } + + private var timelineForecasts: [LocationForecast] { + report.ranking.primary.compactMap { + report.forecasts.forecast(for: $0.region, report: report.report) } } } @@ -150,6 +205,17 @@ struct PresenceTimelineList: View { PresenceTimelineList(report: PreviewSupport.plannedStayYearReportModel()) } }, + whereSnapshot( + name: "ShortPlannedStay", + configurations: .fullContentPhoneLightDark, + measurementReadiness: .immediate, + ) { + NavigationStack { + PresenceTimelineList(report: PreviewSupport.plannedStayYearReportModel( + plannedThroughDay: CalendarDay(year: 2026, month: 7, day: 24), + )) + } + }, whereSnapshot( name: "PlannedStayHidden", configurations: .fullContentPhoneLightDark, @@ -161,6 +227,28 @@ struct PresenceTimelineList: View { )) } }, + whereSnapshot( + name: "PlannedStayDifferentRegion", + configurations: .fullContentPhoneLightDark, + measurementReadiness: .immediate, + ) { + NavigationStack { + PresenceTimelineList(report: PreviewSupport.plannedStayYearReportModel( + plannedRegion: .california, + )) + } + }, + whereSnapshot( + name: "PlannedStayAfterRecordingGap", + configurations: .fullContentPhoneLightDark, + measurementReadiness: .immediate, + ) { + NavigationStack { + PresenceTimelineList(report: PreviewSupport.plannedStayYearReportModel( + recordedThroughDay: CalendarDay(year: 2026, month: 7, day: 12), + )) + } + }, ] } } diff --git a/Where/WhereUI/Sources/Shared/WhereStylesheet.swift b/Where/WhereUI/Sources/Shared/WhereStylesheet.swift index 0a371a7e0..b6e674fd8 100644 --- a/Where/WhereUI/Sources/Shared/WhereStylesheet.swift +++ b/Where/WhereUI/Sources/Shared/WhereStylesheet.swift @@ -1527,6 +1527,12 @@ extension WhereStylesheet { var hatchSpacing: CGFloat var hatchLineWidth: CGFloat var labelOpacity: Double + var transitionHeight: CGFloat + var joinedBaseHeight: CGFloat + var joinedVerticalPadding: CGFloat + var joinedLabelSpacing: CGFloat + var joinedCountHorizontalPadding: CGFloat + var joinedCountVerticalPadding: CGFloat } static let standard = TimelineStyle( @@ -1582,6 +1588,12 @@ extension WhereStylesheet { hatchSpacing: 8, hatchLineWidth: 1, labelOpacity: 0.7, + transitionHeight: 16, + joinedBaseHeight: 32, + joinedVerticalPadding: 8, + joinedLabelSpacing: 5, + joinedCountHorizontalPadding: 8, + joinedCountVerticalPadding: 4, ), ) } diff --git a/Where/WhereUI/Tests/WhereStylesheetTests.swift b/Where/WhereUI/Tests/WhereStylesheetTests.swift index dc0bcadee..a7d75d2be 100644 --- a/Where/WhereUI/Tests/WhereStylesheetTests.swift +++ b/Where/WhereUI/Tests/WhereStylesheetTests.swift @@ -443,6 +443,12 @@ struct WhereStylesheetTests { #expect(planned.hatchSpacing == 8) #expect(planned.hatchLineWidth == 1) #expect(planned.labelOpacity == 0.7) + #expect(planned.transitionHeight == 16) + #expect(planned.joinedBaseHeight == 32) + #expect(planned.joinedVerticalPadding == 8) + #expect(planned.joinedLabelSpacing == 5) + #expect(planned.joinedCountHorizontalPadding == 8) + #expect(planned.joinedCountVerticalPadding == 4) } @Test func regionMapStyle() {