Compare commits

..

1088 Commits

Author SHA1 Message Date
Eric Bailey df5ff1b72e Override scrollRef types on profile 2023-11-17 10:46:59 -06:00
Eric Bailey 54bf784faf Fix some low-hanging type errors 2023-11-17 10:42:14 -06:00
dan 6c8f043e37 Freeze inactive screens (#1949) 2023-11-16 22:20:08 -08:00
dan a8aed2c38e Use non-reactive callbacks for some methods (#1948)
* Use non-reactive callbacks for some methods

* Remove unnecessary state

* Use non-reactive callbacks for modals
2023-11-16 22:18:37 -08:00
dan a10adf8908 Add some memoization (#1947) 2023-11-16 18:52:41 -08:00
Paul Frazee e358c3cc30 Improve notification load behaviors (#1943)
* Dont use the stale cache for notifs-feed

* Add a delay to marking all read to avoid marking upcoming posts as read

* Trigger automatic notifications refresh when navigating to the tab, in certain conditions
2023-11-16 18:27:05 -08:00
Paul Frazee 357c752a21 Move the current agent to a global and reset RQ queries on agent change (#1946) 2023-11-16 18:26:22 -08:00
dan 3043b32468 Fix jumpy modal regression (#1945) 2023-11-16 18:01:51 -08:00
Paul Frazee c03c744008 Notifs refresh improvement (#1941)
* Show loading state in notifications

* Fix invalidation of notifs

* Move spinner below header

* Fix: dont show spinner on initial load
2023-11-16 15:50:57 -08:00
Eric Bailey 6f57192bd5 Revise cache times (#1942)
(cherry picked from commit 1be2060a2b)
2023-11-16 15:43:53 -08:00
Paul Frazee 1952705ded Disable refetchOnWindowFocus on react-query (#1936) 2023-11-16 15:42:37 -08:00
Eric Bailey 6dfb2a232f Caching heuristics (#1938)
* Tempfix profile load

* First pass at staleTime
2023-11-16 15:28:50 -08:00
dan f89dc63801 Add profile loading shim (#1940) 2023-11-16 23:23:58 +00:00
dan 22df70b3cc Fix blank feed (#1935)
* Split feed into two components

* Rm unused

* Add a spinner
2023-11-16 14:37:48 -08:00
dan 8475312422 Add a mutation queue to fix race conditions in toggles (#1933)
* Prototype a queue

* Track both current and pending actions

* Skip unnecessary actions

* Commit last confirmed state to shadow

* Thread state through actions over time

* Fix the logic to skip redundant mutations

* Track status

* Extract an abstraction

* Fix standalone mutations

* Add types

* Move to another file

* Return stable function

* Clean up

* Use queue for muting

* Use queue for blocking

* Convert other follow buttons

* Don't export non-queue mutations

* Properly handle canceled tasks

* Fix copy paste
2023-11-16 14:01:01 -08:00
Paul Frazee 54faa7e176 Remove deprecated models and mobx usage (react-query refactor) (#1934)
* Update login page to use service query

* Update modal to use session instead of store

* Move image sizes cache off store

* Update settings to no longer use store

* Update link-meta fetch to use agent instead of rootstore

* Remove deprecated resolveName()

* Delete deprecated link-metas cache

* Delete deprecated posts cache

* Delete all remaining mobx models, including the root store

* Strip out unused mobx observer wrappers
2023-11-16 12:53:43 -08:00
Paul Frazee e637798e05 Refactor account-creation to use react-query and a reducer (react-query refactor) (#1931)
* Refactor account-creation to use react-query and a reducer

* Add translations

* Missing translate
2023-11-16 11:16:31 -08:00
Paul Frazee 9f7a162a96 Refactor app passwords to use react-query (#1932) 2023-11-16 11:11:56 -08:00
Eric Bailey 310a7eaca7 Some me model cleanup (#1928)
* Replace me model in post dropdown btn

* Replace delete account logic

* Replace me model in bottom bar web

* Replace me model in bottom bar

* Replace me model in reply prompt

* Better fallback

* Fix reference

* Fix bad ref in bottom bar
2023-11-16 10:54:03 -08:00
Eric Bailey a652b52b88 Refactor ChangeHandle modal (#1929)
* Refactor ChangeHandle to use new methods

* Better telemetry

* Remove unused logic

* Remove caching

* Add error message

* Persist service changes, don't fall back on change handle
2023-11-16 09:16:16 -08:00
Eric Bailey e6efeea7c0 Refactor invites modal (#1930)
* Refactor invites modal

* Replace in drawer

* Delete stuff from me model
2023-11-16 08:40:31 -08:00
Paul Frazee 8a1fd160e6 Update UserInfoText component (#1927) 2023-11-16 08:19:08 -08:00
Paul Frazee a84b2f9f2f Close active elems (react-query refactor) (#1926)
* Refactor closeAny and closeAllActiveElements

* Add close lightbox

* Switch to hooks

* Fixes
2023-11-16 08:18:59 -08:00
Eric Bailey 0de8d40981 Misc cleanup (#1925)
* Remove unused prefs

* Cleanup

* Remove my-follows cache

* Replace moderationOpts in ProfileCard comp

* Replace moderationOpts in FeedSlice

* Remove preferences model
2023-11-15 18:35:58 -08:00
Paul Frazee e749f2f3a5 Factor lightbox out into hook/context (#1919) 2023-11-15 18:17:03 -08:00
Paul Frazee 03b20c70e4 Fix memo params causing tuner to lose state (#1924) 2023-11-15 17:42:15 -08:00
Eric Bailey 952f5033d0 Fixes analytics init on web (#1923) 2023-11-15 17:37:55 -08:00
Eric Bailey 610eeecd26 Fix onboarding follows (#1922) 2023-11-15 17:37:32 -08:00
Eric Bailey 8857ba70c6 Improve types (#1921) 2023-11-15 17:23:59 -08:00
Eric Bailey d8b26edb56 Remove post thread (#1920)
* Delete post-thread model

* Remove post-thread-item

* Remove unused types
2023-11-15 17:18:45 -08:00
Paul Frazee 6616b2bff0 Shell behaviors update (react-query refactor) (#1915)
* Move tick-every-minute into a hook/context

* Move soft-reset event out of the shell model

* Update soft-reset listener on new search page

* Implement session-loaded and session-dropped events

* Update analytics and push-notifications to use new session system
2023-11-15 17:17:50 -08:00
Eric Bailey f23e9978d8 Update post embed fetching to use new methods (#1916)
* Update post embed fetching to use new methods

* Use session agent
2023-11-15 17:09:13 -08:00
Eric Bailey 9bcd00b831 No need to await follows anymore, fix cold load search (#1918) 2023-11-15 17:08:08 -08:00
Eric Bailey fe1a7183fc Translate some things on search (#1917) 2023-11-15 19:00:43 -06:00
Eric Bailey 22b76423a0 Search page (#1912)
* Desktop web work

* Mobile search

* Dedupe suggestions

* Clean up and reorg

* Cleanup

* Cleanup

* Use Pager

* Delete unused code

* Fix conflicts

* Remove search ui model

* Soft reset

* Fix scrollable results, remove observer

* Use correct ScrollView

* Clean up layout

---------

Co-authored-by: Paul Frazee <pfrazee@gmail.com>
2023-11-15 15:55:28 -08:00
Paul Frazee d5ea31920c Autocomplete updates (react-query refactor) (#1911)
* Unify the autocomplete code; drop fuse

* Persist autocomplete results while they're in progress

* Commit lockfile

* Use ReturnType helper

---------

Co-authored-by: Eric Bailey <git@esb.lol>
2023-11-15 14:39:22 -08:00
dan 839e8e8d0a Post PostLikedBy and PostRepostedBy to RQ (#1913)
* Port PostRepostedBy to RQ

* Port PostLikedBy to RQ
2023-11-15 14:04:25 -08:00
dan e699df21c6 Port Profile Followers/Follows to RQ (#1893)
* Port user followers to RQ

* Port user follows to RQ

* Start porting FollowButton to RQ

* Fix RQ key

* Check pending

* Fix shadow and pending states

* Rm unused

* Remove last usage of useFollowProfile
2023-11-14 17:55:54 -08:00
Eric Bailey d1cb74febe Desktop user autocomplete search (#1906)
* Fix notification provider order, add comments

* Remove log

* Add actor typeahead handling

* Trim down desktop search styles and hooks

* Clean up moderation
2023-11-14 17:51:23 -08:00
Eric Bailey ab1ce078ec Fix openCamera, openCropped, lightbox model, and image model usages (#1908) 2023-11-14 17:46:46 -08:00
Eric Bailey 7d5e01f433 Fix profile list (#1907) 2023-11-14 17:45:38 -08:00
Paul Frazee e08d06f263 Fix image order in 4-image post embeds (close #1895) (#1904) 2023-11-14 17:10:58 -06:00
Eric Bailey 68767d597e Fix notification provider order, add comments (#1905)
* Fix notification provider order, add comments

* Remove log
2023-11-14 16:54:02 -06:00
Eric Bailey ab6e3f2c5d Fix self mention, resolve handle (#1903)
* Fix self mention, resolve handle

* Use queryClient

* Fix type

* Remove staleTime
2023-11-14 14:16:56 -06:00
Paul Frazee 8e4a3ad5b6 Add Shadow type (#1900) 2023-11-14 12:10:39 -08:00
Eric Bailey 00f8c8b06d Fix root sibling context issue (#1902) 2023-11-14 10:46:37 -08:00
Paul Frazee 0a26e78dcb Composer update (react-query refactor) (#1899)
* Move composer state to a context

* Rework composer to use RQ

---------

Co-authored-by: Eric Bailey <git@esb.lol>
2023-11-14 10:41:55 -08:00
Paul Frazee c687172de9 Fix poll latest loop (#1901)
* Avoid pollLatest loop

* Set poll interval on profile feeds
2023-11-14 12:33:05 -06:00
Eric Bailey 3fde1bea1b Handle end of feed (#1898)
* Handle end of feed

* Add isFetching
2023-11-14 11:28:40 -06:00
Eric Bailey 4355f0fd9a Refactor onboarding suggested follows (#1897)
* Refactor onboarding suggested follows

* Fix error state, track call

* Remove todo

* Use flatmap

* Add additional try catch

* Remove todo
2023-11-14 11:25:37 -06:00
Paul Frazee a81c4b68fa Update Muted and Blocked accounts screens (react-query refactor) (#1892)
* Add my-blocked-accounts and my-muted-accounts queries

* Update ProfileCard to use the profile shadow cache and useModerationOpts

* Update blocked accounts and muted accounts screens
2023-11-13 17:30:56 -08:00
Paul Frazee 0501c2be77 Profile cleanup (react-query refactor) (#1891)
* Only fetch profile tab content when focused

* Fix keys

* Add missing behaviors to post tabs

* Delete old profile mobx model
2023-11-13 15:12:41 -08:00
Paul Frazee 47204d9551 Update profile preview to use react-query (#1890) 2023-11-13 14:57:53 -08:00
Paul Frazee 9fca7b3af6 Add feedgens tab to profile (#1889) 2023-11-13 14:46:19 -08:00
Eric Bailey b04748e703 Factor out store from post-feed query (#1888) 2023-11-13 14:07:18 -08:00
Eric Bailey 06eb8b9a4c Factor our feed source model (#1887)
* Refactor first onboarding step

* Replace old FeedSourceCard

* Clean up CustomFeedEmbed

* Remove discover feeds model

* Refactor ProfileFeed screen

* Remove useCustomFeed

* Delete some unused models

* Rip out more prefs

* Factor out treeView from thread comp

* Improve last commit
2023-11-13 13:53:57 -08:00
Paul Frazee a01463788d More profile refactor updates (#1886)
* Update the profile avatar lightbox

* Update profile editor

* Add dynamic likes tab

* Add dynamic feeds and lists tabs

* Implement lists listing on profiles
2023-11-13 13:29:33 -08:00
dan 8217761363 Fix jump for web pager (#1884) 2023-11-13 13:03:47 -08:00
dan bb4ed3cd49 Fix lint on main (#1885) 2023-11-13 13:01:26 -08:00
dan e1938931e0 Refactor profile screen to use new pager and react-query (#1870)
* Profile tabs WIP

* Refactor the profile screen to use react-query (WIP)

* Add the profile shadow and get follow, mute, and block working

* Cleanup

---------

Co-authored-by: Paul Frazee <pfrazee@gmail.com>
2023-11-13 10:35:15 -08:00
Eric Bailey c3edde8ac6 More prefs cleanup (#1882)
* Update useHomeTabs

* Use queryClient to fetch

* Replace useHomeTabs and useDesktopRightNavItems

* Fix type error

* Some cleanup
2023-11-13 10:21:53 -08:00
Eric Bailey 37a2204483 Update clear account behavior, ensure agent is set (#1881) 2023-11-13 08:20:38 -08:00
Paul Frazee b445c15cc9 Refactor notifications to use react-query (#1878)
* Move broadcast channel to lib

* Refactor view/com/post/Post and remove temporary 2 components

* Add useModerationOpts hook

* Refactor notifications to use react-query

* Fix: only trigger updates in useModerationOpts when the values have changed

* Implement unread notification tracking

* Add moderation filtering to notifications

* Handle native/push notifications

* Remove dead code

---------

Co-authored-by: Eric Bailey <git@esb.lol>
2023-11-12 18:13:11 -08:00
Eric Bailey c584a3378d Refactor My Feeds (#1877)
* Refactor My Feeds screen

* Remove unused feed UI models

* Add back PTR
2023-11-12 16:26:02 -08:00
Paul Frazee d9e0a927c1 Refactor lists to use new queries (#1875)
* Refactor lists queries to react-query

* Delete old lists-list model

* Implement list, list-members, and list-memberships react-queries

* Update CreateOrEditList modal

* First pass at my-follows and actor-autocomplete queries

* Update ListAddUserModal to use new queries, change to ListAddRemoveUsersModal

* Update UserAddRemoveLists modal

* Remove old TODO

* Fix indent, autocomplete query

* Add a todo

---------

Co-authored-by: Eric Bailey <git@esb.lol>
2023-11-12 12:45:25 -08:00
Eric Bailey 05b728fffc Eric/preferences (#1873)
* Add initial preferences query, couple mutations

* Remove unused

* Clean up labels, migrate getModerationOpts

* Add birth date handling

* Migrate feed prefs

* Migrate thread view prefs

* Migrate homeFeed to use existing key name

* Fix up saved feeds in response, no impl yet

* Migrate saved feeds to new hooks

* Clean up more of preferences

* Fix PreferencesThreads load state

* Fix modal dismissal

* Small spacing fix

---------

Co-authored-by: Paul Frazee <pfrazee@gmail.com>
2023-11-12 11:31:11 -08:00
Paul Frazee c8c308e31e Refactor feeds to use react-query (#1862)
* Update to react-query v5

* Introduce post-feed react query

* Add feed refresh behaviors

* Only fetch feeds of visible pages

* Implement polling for latest on feeds

* Add moderation filtering to slices

* Handle block errors

* Update feed error messages

* Remove old models

* Replace simple-feed option with disable-tuner option

* Add missing useMemo

* Implement the mergefeed and fixes to polling

* Correctly handle failed load more state

* Improve error and empty state behaviors

* Clearer naming
2023-11-10 15:34:25 -08:00
dan 51f04b9620 Use min height for pager lists and increase it (#1869) 2023-11-10 22:29:12 +00:00
dan 86b4842d67 Fix initial pager gap after fast scroll (#1868) 2023-11-10 21:23:17 +00:00
dan 91f8a23fbc Scroll sync in the pager without jumps (#1863) 2023-11-10 19:54:33 +00:00
dan 65def37165 Push useAnimatedScrollHandler down everywhere to work around bugs (#1866)
* Move useOnMainScroll handlers to leaves

* Force Feed to always take handlers

* Pass handlers from the pager
2023-11-10 19:00:46 +00:00
Eric Bailey e0e5bc8fd8 Fix type errors (#1867) 2023-11-10 18:20:33 +00:00
Eric Bailey 6513055d02 Merge pull request #1860 from bluesky-social/eric/startup
Web login/signup and shell
2023-11-10 11:31:36 -06:00
Eric Bailey 436a14eabb Pare down session as much as possible 2023-11-10 09:59:04 -06:00
Eric Bailey d0d93168d4 Clean up isDirty handling 2023-11-10 08:59:39 -06:00
dan 8d7475c130 Work around web stale closure bug in Reanimated (#1865) 2023-11-10 14:58:13 +00:00
Eric Bailey 499021229a Remove error states, just best effort 2023-11-10 08:53:23 -06:00
Eric Bailey 60386f8f07 Swap order 2023-11-10 08:48:15 -06:00
Eric Bailey b0c9cce5c3 Follow conventions for query, use isDirty flag in session store to avoid unneccessary writes 2023-11-10 08:46:45 -06:00
Eric Bailey 742f53d1ec Comments 2023-11-09 20:46:45 -06:00
Eric Bailey 2d7b89c6a1 Cleanup getProfile 2023-11-09 20:39:54 -06:00
Eric Bailey ab878ba9a6 Web login/signup and shell 2023-11-09 20:35:17 -06:00
dan 487d871cfd Pull useAnimatedScrollHandler back up (#1858)
* Revert "Pull animated scroll handler down from pager"

This reverts commit ecebb78e40.

* Only handle onScroll for current page
2023-11-09 23:47:54 +00:00
Paul Frazee fb4f5709c4 Refactor post threads to use react query (#1851)
* Add post and post-thread queries

* Update PostThread components to use new queries

* Move from normalized cache to shadow cache model

* Merge post shadow into the post automatically

* Remove dead code

* Remove old temporary session

* Fix: set agent on session creation

* Temporarily double-login

* Handle post-thread uri resolution errors
2023-11-09 15:35:25 -08:00
Eric Bailey 625cbc435f First pass at a session handler (#1850)
* First pass at a session handler

* TODOs

* Fix recursion

* Couple more things

* Add back resume session concept

* Handle ready

* Cleanup of initial loading states

* Handle init failure

* Cleanup

* Remove account

* Add updateCurrentAccount

* Remove log

* Cleanup

* Integrate removeAccount

* Add hasSession

* Add to App.native, harden migration

* Use effect to persist data
2023-11-09 15:14:51 -08:00
dan 664e7a91a9 Fix tab alignment on the web (#1857) 2023-11-09 21:56:25 +00:00
dan c627a766cd Show tabs when swiping feeds (#1856) 2023-11-09 21:45:31 +00:00
dan 7a55ca6133 Sync top/bottom bar disappearance to the scroll (#1855)
* Disable existing code that toggles shell

* Make shell mode a float

* Translate based on the gesture

* Track header and footer heights

* Add web support

* Fix types and cleanup

* Add back isScrolled logic

* Add comments
2023-11-09 12:15:05 -08:00
Ansh 1dcf882619 Hotfix internationalization on mobile (#1854)
* hotfix internationalization on mobile

* remove comment
2023-11-09 10:49:46 -08:00
Ansh 4c7850f8c4 Internationalization & localization (#1822)
* install and setup lingui

* setup dynamic locale activation and async loading

* first pass of automated replacement of text messages

* add some more documentaton

* fix nits

* add `es` and `hi`locales for testing purposes

* make accessibilityLabel localized

* compile and extract new messages

* fix merge conflicts

* fix eslint warning

* change instructions from sending email to opening PR

* fix comments
2023-11-09 10:04:16 -08:00
dan 82059b7ee1 Hide/show header and footer without re-renders, take two (#1849)
* Remove callsites using the state value

* Remove unused code

* Change shell mode without re-renders

* Adjust "write your reply" for mode
2023-11-09 00:25:27 +00:00
Eric Bailey bd531f2344 Pull language methods into api context (#1847)
* Pull language methods into api context

* Rename for consistency
2023-11-08 11:55:38 -08:00
Eric Bailey f18b15241a Add modal state provider, replace usage except methods (#1833)
* Add modal state provider, replace usage except methods

* Replace easy spots

* Fix sticky spots

* Replace final usages

* Memorize context objects

* Add more warnings
2023-11-08 10:34:10 -08:00
Paul Frazee 5eadadffbf State layer cleanup: move alt-text-required into preferences and fix a bug in reminders (#1845)
* Move alt-text-required into preferences

* Fix bug: reminders now using new onboarding state
2023-11-08 09:46:07 -08:00
Paul Frazee 5843e212c0 Move language preferences to new persistence + context (#1837) 2023-11-08 09:38:28 -08:00
Paul Frazee e75b2d508b Move invite-state to new persistence + context and replace the notifications with just showing uses in the modal (#1840) 2023-11-08 09:10:59 -08:00
Paul Frazee 74f8390f1d Move muted threads to new persistence + context (#1838) 2023-11-08 09:08:42 -08:00
Paul Frazee 4afed4be28 Move onboarding state to new persistence + reducer context (#1835) 2023-11-08 09:04:06 -08:00
Paul Frazee 3a211017d3 Move require alt-text to new persistence + context (#1839) 2023-11-08 08:52:01 -08:00
Paul Frazee 2acc88e78d Move reminders to new persisted state layer (#1834) 2023-11-07 16:39:13 -08:00
Eric Bailey 96d8faf4b0 Add persistent state provider (#1830)
* Add persistent state provider

* Catch write error

* Handle read errors, update error msgs

* Fix lint

* Don't provide initial state to loader

* Remove colorMode from shell state

* Idea: hook into persisted context from other files

* Migrate settings to new hook

* Rework persisted state to split individual contexts

* Tweak persisted schema and validation

---------

Co-authored-by: Paul Frazee <pfrazee@gmail.com>
2023-11-07 14:06:17 -08:00
Eric Bailey bfe196bac5 Extract shell state into separate context (#1824)
* WIP

* Add shell state

* Integrate new shell state for drawer and minimal shell mode

* Replace isDrawerSwipeDisabled

* Split shell state into separate contexts to avoid needless re-renders

* Fix typo

---------

Co-authored-by: Paul Frazee <pfrazee@gmail.com>
2023-11-07 11:37:47 -08:00
Noritada Kobayashi 7158157f5f Correct meta tag attributes (#1829) 2023-11-07 09:56:44 -08:00
dan 7b2a7db83c Pull animated scroll handler down from pager (#1827) 2023-11-07 08:46:39 -08:00
dan fa821943da Use Sentry only for errors (#1776)
* Use Sentry only for errors

* Fix merge
2023-11-07 04:30:00 +00:00
Eric Bailey 7ffbee18b5 Fix removal of old lists from saved feeds (#1823)
* Fix removal of old lists from saved feeds

* Fix saved feed removal race condition
2023-11-06 18:51:46 -08:00
dan a4baf14e4b Fix accidental unwrapped strings (#1826) 2023-11-06 14:42:25 -08:00
dan d715246e26 Fix sticky pager jumps (#1825)
* Defer showing pager content until its header settles

* Introduce the concept of headerOnlyHeight

* Keep headerOnlyHeight in state, make headerHeight derived

* Hide content until *both* header (only) and tabbar are measured

* Hide tabbar to read its layout earlier

* Give consistent keys to pages
2023-11-06 22:30:10 +00:00
dan 4c00fc576d Fix minor list layout bugs (#1810)
* Fix accidental border on mobile

* Fix lists list to take full screen
2023-11-06 17:15:01 +00:00
Eric Bailey 95b5e642dc Merge pull request #1814 from bluesky-social/eric/replace-logger
Remove old logger
2023-11-04 14:00:38 -05:00
Eric Bailey f6fe980a64 Remove old logger 2023-11-04 13:43:28 -05:00
Eric Bailey f51351e80d Replace all logs with new logger 2023-11-04 13:42:36 -05:00
Eric Bailey e49a3d8a56 Merge pull request #1813 from bluesky-social/eric/app-903-extract-logger-into-singleton
Add new logger
2023-11-04 13:12:46 -05:00
Eric Bailey 0c76866757 Add logDump tests, ensure Log screen is reversed 2023-11-04 13:09:12 -05:00
Eric Bailey 7e29ebbadb Fix other error logs while I'm at it 2023-11-04 12:58:50 -05:00
Eric Bailey df0dcf32f9 Fix immediate TS errors 2023-11-04 12:42:27 -05:00
Eric Bailey 5fd41ad5a2 Comment out sentry in prod for now 2023-11-04 12:28:30 -05:00
Eric Bailey fd93bf2146 Add new logger 2023-11-04 12:18:56 -05:00
Paul Frazee 46c2564e65 Increment iOS buildnumber & android version code for release 2023-11-03 16:44:59 -07:00
Paul Frazee ebad6d2b1a ListAddUser modal UX improvements (#1809)
* typo

* Add loading state to ListAddUser

* Improve UI/UX of ListAddUser
2023-11-03 16:44:00 -07:00
Paul Frazee 4de852ded8 Refresh on scroll-to-top by active section press (#1807) 2023-11-03 14:53:47 -07:00
Paul Frazee 445f976881 Improved list and feed errors (#1798)
* Fix error-state rendering of ProfileList

* Unsave/unpin lists on delete

* Improve handling of failing feedgens

* Only show 'remove' btn on feed DNE
2023-11-03 14:18:44 -07:00
Eric Bailey 691af26895 Add subpath imports (#1808) 2023-11-03 14:17:40 -07:00
Paul Frazee 047f74c67d Fix copyright report link (#1799) 2023-11-02 22:09:31 -07:00
Paul Frazee 9437d743e2 Bump ios build number 2023-11-02 19:55:15 -07:00
Paul Frazee 846c130706 Lookup did for my profile at screen to avoid bad actor error (#1800) 2023-11-02 19:54:43 -07:00
dan 8747c215b1 Work around a startup stall caused by expo-image on low-end Android (#1801)
* Use plain RN image for startup-blocking UI

* Add comments
2023-11-02 19:33:18 -07:00
Paul Frazee 5e2025e60a Bump @atproto/api@0.6.23 2023-11-02 18:27:20 -07:00
Eric Bailey f258289be9 Treat PHP pages as HTML (#1797) 2023-11-02 15:16:31 -07:00
Paul Frazee 0f4bfcb05d Replace draglist due to upstream errors (#1795)
* Replace draggable flatlist with simple sort buttons

* Remove react-native-draggable-flatlist dep

* Fix hitslops

* Update lockfile

* Remove bad flex:1
2023-11-02 12:09:57 -07:00
dan 0e8723c3bb Patch facebook/metro#1130 to profile module init (#1796) 2023-11-02 18:48:07 +00:00
Paul Frazee 237bce391b Merge branch 'main' of github.com:bluesky-social/social-app into main 2023-11-02 09:43:47 -07:00
Paul Frazee c8b9407e0e Merge branch 'flicknow-make-choose-account-scrollable' into main 2023-11-02 09:43:37 -07:00
Paul Frazee b782cd0d06 Fix max-height issues 2023-11-02 09:42:29 -07:00
Paul Frazee 4e5a99d4d3 Merge branch 'make-choose-account-scrollable' of https://github.com/flicknow/social-app into flicknow-make-choose-account-scrollable 2023-11-02 09:34:49 -07:00
Spandan Tripathy d7744c2590 Update build.md (#1785) 2023-11-02 09:27:31 -07:00
Paul Frazee 29bfed78f7 Merge branch 'main' of github.com:bluesky-social/social-app into main 2023-11-01 21:14:47 -07:00
Paul Frazee 232e90c0a0 1.56 2023-11-01 21:14:39 -07:00
Paul Frazee 59271663b5 Store saved feeds on the root store so we can load on init (#1793) 2023-11-01 18:45:59 -07:00
flicknow 5f135aa021 Make choose account form scrollable 2023-11-01 20:55:06 -04:00
Paul Frazee f57a8cf8ba Lists updates: curate lists and blocklists (#1689)
* Add lists screen

* Update Lists screen and List create/edit modal to support curate lists

* Rework the ProfileList screen and add curatelist support

* More ProfileList progress

* Update list modals

* Rename mutelists to modlists

* Layout updates/fixes

* More layout fixes

* Modal fixes

* List list screen updates

* Update feed page to give more info

* Layout fixes to ListAddUser modal

* Layout fixes to FlatList and Feed on desktop

* Layout fix to LoadLatestBtn on Web

* Handle did resolution before showing the ProfileList screen

* Rename the CustomFeed routes to ProfileFeed for consistency

* Fix layout issues with the pager and feeds

* Factor out some common code

* Fix UIs for mobile

* Fix user list rendering

* Fix: dont bubble custom feed errors in the merge feed

* Refactor feed models to reduce usage of the SavedFeeds model

* Replace CustomFeedModel with FeedSourceModel which abstracts feed-generators and lists

* Add the ability to pin lists

* Add pinned lists to mobile

* Remove dead code

* Rework the ProfileScreenHeader to create more real-estate for action buttons

* Improve layout behavior on web mobile breakpoints

* Refactor feed & list pages to use new Tabs layout component

* Refactor to ProfileSubpageHeader

* Implement modlist block and mute

* Switch to new api and just modify state on modlist actions

* Fix some UI overflows

* Fix: dont show edit buttons on lists you dont own

* Fix alignment issue on long titles

* Improve loading and error states for feeds & lists

* Update list dropdown icons for ios

* Fetch feed display names in the mergefeed

* Improve rendering off offline feeds in the feed-listing page

* Update Feeds listing UI to react to changes in saved/pinned state

* Refresh list and feed on posts tab press

* Fix pinned feed ordering UI

* Fixes to list pinning

* Remove view=simple qp

* Add list to feed tuners

* Render richtext

* Add list href

* Add 'view avatar'

* Remove unused import

* Fix missing import

* Correctly reflect block by list state

* Replace the <Tabs> component with the more effective <PagerWithHeader> component

* Improve the responsiveness of the PagerWithHeader

* Fix visual jank in the feed loading state

* Improve performance of the PagerWithHeader

* Fix a case that would cause the header to animate too aggressively

* Add the ability to scroll to top by tapping the selected tab

* Fix unit test runner

* Update modlists test

* Add curatelist tests

* Fix: remove link behavior in ListAddUser modal

* Fix some layout jank in the PagerWithHeader on iOS

* Simplify ListItems header rendering

* Wait for the appview to recognize the list before proceeding with list creation

* Fix glitch in the onPageSelecting index of the Pager

* Fix until()

* Copy fix

Co-authored-by: Eric Bailey <git@esb.lol>

---------

Co-authored-by: Eric Bailey <git@esb.lol>
2023-11-01 16:15:40 -07:00
dan f9944b55e2 Simplify loading emoji data (#1790) 2023-11-01 17:49:07 +00:00
dan 9fb20517ae Remove unused Intl.Segmenter polyfill (#1789) 2023-11-01 16:52:34 +00:00
Eric Bailey 4bf1719092 Merge pull request #1788 from bluesky-social/eric/app-899-remove-icons-from-startup-code
Revise icons startup code
2023-11-01 10:38:37 -05:00
dan 85c26fb5a8 Instrument module init in DEV (#1787) 2023-11-01 15:31:33 +00:00
Eric Bailey b42806a6e0 Revise icons startup code 2023-11-01 10:15:37 -05:00
Samiksha Mishra adb5ce2961 Updated the mistake (#1777)
Corrected the typo error
2023-10-31 08:39:35 -07:00
Tarık b5f36dc7a3 UI/UX improvements in Edit profile screen (#1767)
* Remove cropped area in Edit profile screen iOS

* Hide cancel button when saving changes in EditProfile

* Disable fadeOut animation cancel button for web in EditProfile screen

Since react-native-reanimated is not configured for web support (https://docs.swmansion.com/react-native-reanimated/docs/2.x/fundamentals/web-support/), we are enabling fade out animation for iOS and android solely
2023-10-30 21:01:25 -07:00
Igor Shevchenko b2d4771b5f chore: fix typos in testing.md (#1764) 2023-10-30 20:40:30 -07:00
Eric Bailey ddf0a1cced Filter non-self threads from profile view (#1757) 2023-10-30 20:40:06 -07:00
Paul Frazee 40752982da Merge branch 'main' of https://github.com/haideralipunjabi/social-app into main 2023-10-30 19:26:23 -07:00
Leo Lamprecht 68c809b09a Use more beautiful title separator (#1728) 2023-10-30 19:25:19 -07:00
Pouria Delfanazari 21f7a2a247 Allow back navigation to home on profile header (#1717)
Navigate back to home if there is no navigation history.
2023-10-30 19:23:41 -07:00
Paul Frazee 97ce9a73d2 Merge branch 'main' of github.com:bluesky-social/social-app into main 2023-10-30 19:18:34 -07:00
Paul Frazee 33ea9ccef7 Merge branch 'Tanza3D-improve-post-button' into main 2023-10-30 19:18:24 -07:00
Paul Frazee 33a87ca22e Yep, one pixel 2023-10-30 19:18:02 -07:00
dan ef1d6ee490 Log time to first paint (#1775)
* Log time to first paint

* Fix TS
2023-10-31 02:07:14 +00:00
Paul Frazee 1eb623ee2f Merge branch 'improve-post-button' of https://github.com/Tanza3D/social-app into Tanza3D-improve-post-button 2023-10-30 18:54:29 -07:00
Paul Frazee d5c7b8ceb8 Merge branch 'main' of github.com:bluesky-social/social-app into main 2023-10-30 18:07:33 -07:00
Paul Frazee bae429202a Merge branch 'liby-fix/step2-waitlist-link-accessibility' into main 2023-10-30 18:06:58 -07:00
Paul Frazee 0747193977 Fix a11y prop 2023-10-30 18:06:31 -07:00
dan 4a4106ba45 Don't inline-require Babel runtime helpers (#1774) 2023-10-31 00:59:00 +00:00
Paul Frazee 2577cbf252 Merge branch 'fix/step2-waitlist-link-accessibility' of https://github.com/liby/social-app into liby-fix/step2-waitlist-link-accessibility 2023-10-30 17:50:44 -07:00
Paul Frazee 9f33badfff Merge branch 'main' of github.com:bluesky-social/social-app into main 2023-10-30 17:50:31 -07:00
Paul Frazee e18e7ce34a Merge branch 'liby-bugfix/desktop-web-text-link-media-query-issue' into main 2023-10-30 17:45:07 -07:00
Paul Frazee 9826e7c7b9 Rename component to match new behavior 2023-10-30 17:44:58 -07:00
dan 515c9d5529 Don't compile Hermes-supported features with Babel (#1773)
* Don't transpile Hermes-supported features

* Disable the object spread plugin

We're using Hermes, and Hermes supports object spread.
We can remove this patch when we upgrade Expo because it'll be conditional there.
2023-10-31 00:23:01 +00:00
Paul Frazee 871ded193c Merge branch 'bugfix/desktop-web-text-link-media-query-issue' of https://github.com/liby/social-app into liby-bugfix/desktop-web-text-link-media-query-issue 2023-10-30 17:11:17 -07:00
Paul Frazee e1dcedd87e Merge branch 'elijah-wright-patch-1' into main 2023-10-30 17:08:15 -07:00
Paul Frazee 1acf4969c5 Fix lint 2023-10-30 17:08:07 -07:00
Paul Frazee dbbd71c6d0 Merge branch 'patch-1' of https://github.com/elijah-wright/social-app into elijah-wright-patch-1 2023-10-30 17:03:34 -07:00
dan 84ee64025f Let Metro handle import/export instead of Babel (#1772)
* Let Metro handle import/export instead of Babel

* Keep using Babel for ESM->CJS in tests
2023-10-30 21:54:23 +00:00
Paul Frazee 6c11c0b81d 1.55 2023-10-30 09:58:58 -07:00
dan 7ebf1ed371 Initialize screen modules lazily (#1763)
* Initialize screen modules lazily

* Use getComponent (thanks @ecreeth for the tip)
2023-10-27 19:06:28 +01:00
dan 53afafb04f Remove redundant linking listeners (#1760) 2023-10-26 21:05:48 -07:00
dan 3e5a64b454 Fix white flash on startup (#1759) 2023-10-27 04:06:40 +01:00
dan 46f3265936 Enable inline requires (#1756) 2023-10-26 17:54:15 -07:00
Paul Frazee a1a61ef2e5 Merge branch 'main' of github.com:bluesky-social/social-app into main 2023-10-26 10:40:35 -07:00
Ansh 927cee18d9 Update Analytics (#1743) 2023-10-26 08:37:14 -07:00
Haider Ali Punjabi 7fa0708e0e Added show more for long posts 2023-10-26 19:38:39 +05:30
Ansh c13b6946ba 1721 Update support link (#1722)
* change support page copy

* fix grammar
2023-10-25 10:00:46 -07:00
Ansh ee80cae75f add scroll indicator on web to profile suggested follows card (#1724) 2023-10-25 09:54:52 -07:00
Ansh 1781b3aab7 allow enter key to trigger waitlist signup (#1723) 2023-10-25 09:54:04 -07:00
elliot 3426b24f67 Fixed CropImage modal aspect ratio buttons visibility in dark mode (#1730) 2023-10-25 09:41:56 -07:00
dan 4a4be1de1a Remove MobX from Link (#1745)
* Remove MobX from Link

* Keep memo() though

We previously had observer() apply it automatically, so re-add it
2023-10-25 08:47:21 -07:00
dan 32fbb9dba7 Simplify the avatar component (#1744)
* Copypaste UserAvatar to EditableUserAvatar

* Swap usages with onSelectNewAvatar to EditableUserAvatar

* Split prop types into UserAvatarProps and EditableUserAvatarProps

* Remove dead branches from EditableUserAvatar

* Remove dead branches from UserAvatar
2023-10-25 04:57:34 +01:00
dan c6e28f88e5 Fix four gallery images for Firefox (#1710) 2023-10-18 15:08:12 -04:00
Tanza3D db0de651ba Fix wonky comment 2023-10-18 14:14:45 +01:00
Tanza3D 6848ade046 Shrink to 16 again 2023-10-18 13:53:55 +01:00
Tanza3D de94acb6c8 Improve New Post button 2023-10-18 13:53:00 +01:00
Bryan Lee 78b374b43d Improved accessibility and interaction for waitlist Link in Step2 component 2023-10-15 06:26:56 +08:00
Paul Frazee 01e25c912c 1.54 2023-10-14 10:32:11 -07:00
Ansh 8e9cf182c2 Performance optimization (#1676)
* upgrade sentry to support profiling monitoring

* remove console logs in production builds

* feeds tab bar and bottom bar animation centralized

* refactor FeedPage out of Home

* add script to start in production mode

* move FAB inner to reanimated

* move FABInner back to `Animated` RN animation

* add perf commands

* add testing with Maestro and perf with Flashlight

* fix merge conflicts

* fix resourceClass name in eas.json

* fix onEndReachedThreshold in Feed

* memoize styles

* go back to old styling for LoadLatestBtn

* remove reanimated code from useMinimalShellMode

* move shell animations to hook/reanimated for perf

* fix empty state issue

* make shell animation feel smoother

* make shell animation more smooth

* run animation with autorun

* specify keys for tab bar properly

* remove comments

* remove already imported dep

* fix lint

* add testing instructions

* mock sentry-expo for jest

* fix jest mocks

* Fix the load-latest button on desktop and tablet

* Fix: don't move the FAB in tablet mode

* Fix type error

* Fix tabs bar positioning on tablet

* Fix types

---------

Co-authored-by: Paul Frazee <pfrazee@gmail.com>
2023-10-13 18:54:35 -07:00
dan 9042f503c2 Scope drawer drag workaround to feed pagers (#1694) 2023-10-13 15:15:36 -07:00
dan abfd9a8c0b Toggle lightbox controls on tap (#1687)
* Make the lightbox controls animation smoother

* Toggle controls on tap

* Disable pointer events when hidden
2023-10-13 20:10:27 +01:00
dan f447eaa669 Fix profile preview jump (#1693)
* Add top inset for profile preview to match target screen

* Avoid flicker by waiting for profile screen navigation

* Fix glimmer to align with the content

* A more reliable (but non-scientific) fix for the flash

* Lower the timeout
2023-10-13 20:10:15 +01:00
dan d5ccbd76d5 Disable events on hidden bars (#1686) 2023-10-13 15:29:50 +01:00
dan eba9f8a166 Fix profile layout shift (#1690) 2023-10-13 15:25:16 +01:00
dan 2a1edab6d4 Don't re-render bars when showing/hiding them (#1691)
* Don't re-render bars when showing/hiding them

* Fix more cases

* Use autorun instead of reaction to fix first render
2023-10-13 15:24:28 +01:00
dan 4431cfe2d2 Fix crash when scrolling down on the web (#1684) 2023-10-12 13:15:19 -07:00
dan 997918547c Make shell hide/show animation smoother (#1683)
* Make shell hide/show animation smoother

* Also animate "load latest"
2023-10-12 21:02:17 +01:00
dan c2a1cf4e56 Fix layout shift for multi-image posts (#1673)
* Fix layout shift for multi-image posts

* Add a comment for the hack

* Use margins instead of gap

* Move alt label
2023-10-12 19:14:27 +01:00
Jake Gold d98e3a8b45 bskyweb: add rate limiting to reduce DoSability 2023-10-11 13:00:59 -07:00
Eric Bailey d40d931d86 use new zeed-dom version (#1671) 2023-10-11 09:04:06 -07:00
Paul Frazee 5dd673d1ff 1.53 2023-10-10 17:32:19 -07:00
Eric Bailey 10deddd0ad resolve fork of zeed-dom (#1663) 2023-10-10 16:23:12 -07:00
dan a48c213af5 Port remaining lightbox code to Reanimated (#1669)
* Port remaining lightbox code to Reanimated

* Fix  memoization
2023-10-10 23:48:31 +01:00
Paul Frazee 0b44af38ea Update testrunner to use new dev-env [WIP] (#1575)
* Update testrunner to use new dev-env

* Fix label testcase

* Vendor the dev-infra scripts from the atproto repo for the dev-env server runner

* Bump detox to fix the ios sim control issue

* Use iphone 15 pro for tests

* Ensure the reminders never trigger during tests

* Skip the shell tests due to a crash bug with detox and the drawer
2023-10-10 15:46:27 -07:00
dan aad8d12ede Fix MobX crash for Android lightbox (#1668)
* Fix MobX crash for Android lightbox

* Reorder props to match between forks
2023-10-10 23:02:00 +01:00
dan 209d8b683c Change lightbox to use Pager (#1666)
* Change lightbox to use Pager

* Fix crash issue on ios

---------

Co-authored-by: Paul Frazee <pfrazee@gmail.com>
2023-10-10 22:37:28 +01:00
Ansh aa085b0b14 make empty feed required (#1667) 2023-10-10 14:32:00 -07:00
Paul Frazee 4d450da194 Only warn on links to bsky.app if it represents itself as another url (#1662)
* Only warn on links to bsky.app if it represents itself as another url (close #1652)

* Clean up
2023-10-10 14:02:50 -07:00
Paul Frazee e878da04a1 Fix keyboard double pad issue in email change & verify modals (#1664) 2023-10-10 14:02:31 -07:00
Eric Bailey fc28fc639f Don't highlight tags in composer yet (#1665) 2023-10-10 13:44:27 -07:00
Paul Frazee d68b4ca856 Fix: fetch follows on desktop search for typeahead (#1660) 2023-10-10 12:27:32 -07:00
dan 60c0db03f7 Remove duplicate modal container (#1661) 2023-10-10 11:57:14 -07:00
dan d47ff542da Drive-by lightbox refactors (#1659)
* Remove dead code from lightbox

* Rename imageIndex prop to initialImageIndex

* Rename currentImageIndex to imageIndex
2023-10-10 11:07:21 -07:00
Paul Frazee bc2c44cb98 Only prompt users once to verify email (according to local storage) close #1657 (#1658) 2023-10-10 10:57:09 -07:00
Paul Frazee 098f4b526e Revert "Fix invite codes flash on desktop, use loading placeholder (#1591)" (#1656)
This reverts commit 9278822088.
2023-10-10 10:08:04 -07:00
dan f452ce74f4 Refactor iOS lightbox to Reanimated (#1645)
* Remove unnecessary transform logic

* Switch iOS swipe-to-dimiss to Reanimated
2023-10-10 10:04:38 +01:00
Paul Frazee 832b05b64a Bump package.json to 1.52 2023-10-09 15:24:23 -07:00
Paul Frazee acb003d7e1 Merge branch 'main' of github.com:bluesky-social/social-app into main 2023-10-09 14:58:40 -07:00
dan ce0f796008 Remove unnecessary opacity logic (#1646) 2023-10-09 22:29:56 +01:00
Muneyuki Noguchi b12cfbe90b Fix typo in image.ts (#1638) 2023-10-09 12:29:16 -07:00
Raku d85bbc1a28 fix typo README.md (#1631) 2023-10-09 11:55:15 -07:00
Toni Alatalo c43a0d8160 Typo fix in README.md: "small about" -> "small amount" (#1639) 2023-10-09 11:48:57 -07:00
Elijah Wright c39352f859 readd hint 2023-10-08 14:30:57 -07:00
Elijah Wright 0122cdb0b6 fix accessibility in AutoSizedImage 2023-10-08 14:29:20 -07:00
Paul Frazee 9aff2597cf Bump build revision 2023-10-06 18:02:22 -07:00
Paul Frazee 77b21dfb1c Fix bug that prevents unfollowing (#1633) 2023-10-06 18:01:57 -07:00
Paul Frazee 9cff82448f 1.52 2023-10-06 08:48:39 -07:00
bnewbold 34f9d12987 README: tweaks to high-level context (#1625) 2023-10-06 08:45:41 -07:00
dan d2f11f8e36 Fix stuck lightbox header after double tap (#1627) 2023-10-05 20:22:32 -07:00
Paul Frazee 3b78d3227f Fix: add padding to the spinner bottom while loading threads (#1626) 2023-10-05 20:10:43 -07:00
dan 64153067e3 Rewrite Android lightbox (#1624) 2023-10-05 19:54:36 -07:00
Paul Frazee 8366fe2c4a Dont trim before posting (close #1621) (#1622)
* Dont trim before posting (close #1621)

* Tweak: do trim end
2023-10-05 19:08:20 -07:00
Paul Frazee b74a0e0fe2 Only listen to back button on android (#1623) 2023-10-05 18:29:41 -07:00
Paul Frazee bd7db8af26 Improve typeahead search with inclusion of followed users (temporary solution) (#1612)
* Update follows cache to maintain some user info

* Prioritize follows in composer autocomplete

* Clean up logic and add new autocomplete to search

* Update follow hook
2023-10-05 16:44:05 -07:00
Paul Frazee 19f8389fc7 Slightly smaller highlighted post text (#1608) 2023-10-05 16:13:49 -07:00
Paul Frazee 0168aada37 Pull upstream bugfixes to bottom-sheet (#1606)
* Bump bottom-sheet to latest for bugfixes

* Small tweak to the account switcher
2023-10-05 15:55:31 -07:00
dan 4ec5fabdd1 Fix animations and gestures getting reset on state updates in the lightbox (#1618)
* Fix translation resetting on state update

* Copy getImageStyles into iOS and Android forks

* Fix opacity resetting on state update
2023-10-05 15:52:04 -07:00
dan 260b03a05c Remove unused lightbox options (#1616)
* Inline lightbox helpers

* Delete unused useImagePrefetch

* Delete unused long press gesture

* Always enable double tap

* Always enable swipe to close

* Remove unused onImageIndexChange

* Inline custom Hooks into ImageViewing

* Declare LightboxFooter outside Lightbox

* Add more TODO comments

* Inline useDoubleTapToZoom

* Remove dead utils, move utils used only once
2023-10-05 15:28:56 -07:00
Paul Frazee eb7306b165 Profile UI tweaks (#1607)
* Use inverted color instead of primary color for press state

* Fix: add missing border to empty state
2023-10-04 13:28:07 -07:00
Patroll 9278822088 Fix invite codes flash on desktop, use loading placeholder (#1591)
* Fix invite codes flashing untrue value before loaded

* Add loading placeholder for right nav invites
2023-10-04 10:31:43 -07:00
Paul Frazee 2ba0c6a711 Update to react-native@0.72.5 (#1599)
* Update to react-native@0.72.5

* Fix the patch
2023-10-04 10:17:13 -07:00
Baasbase c6da98c818 Fixed a typo on the onboarding recommended screen (#1604) 2023-10-04 08:58:01 -07:00
Paul Frazee b1a1bae02e Onboarding & feed fixes (#1602)
* Fix: improve the 'end of feed' detection condition

* Fix the feeds link on mobile in the empty state

* Align the following empty state better on web

* Dont autofocus the search input in the search tab

* Fix the error boundary render

* Add 'end of feed' CTA to following feed

* Reduce the default feeds to discover now that we have feed-selection during onboarding

* Fix case where loading spinner fails to stop rendering in bottom of feed

* Fix: dont show loading spinner at footer of feed when refreshing

* Fix: dont fire reminders during onboarding

* Optimize adding feeds and update to mirror the api behaviors more closely

* Use the lock in preferences to avoid clobbering in-flight updates

* Refresh the feed after onboarding to ensure content is visible

* Remove the now-incorrect comment

* Tune copy
2023-10-04 08:57:23 -07:00
Bryan Lee 2ca673d7f5 Replace isDesktop with isWeb in DesktopWebTextLink component
- Replaced `useWebMediaQueries().isDesktop` with `isWeb` in `DesktopWebTextLink` component to fix inconsistent behavior due to media query changes.
- `isWeb` doesn't rely on media queries, offering stable judgement and avoiding fluctuation when adjusting the browser window.
2023-10-04 21:47:19 +08:00
Paul Frazee a76fb78d53 Improve time to content in the search page (#1603)
* Improve time to content in the search page

* Improve loading state clarity
2023-10-03 20:01:09 -07:00
Paul Frazee 847d4d19b5 Fix a potential reference error in bottombarweb (#1600) 2023-10-03 19:55:44 -07:00
Paul Frazee 6598fca79d Fix: only use scroll-positioning control on thread when looking at replies (#1587) 2023-10-03 19:55:28 -07:00
Paul Frazee 588659380a Add an illustration to the email verification reminder (#1586)
* Add an illustration to the email verification reminder

* Set height on svg to avoid overflow issues in android
2023-10-03 19:51:57 -07:00
Paul Frazee 001b29a7e3 Fix bad reference when image picker is canceled (#1598) 2023-10-03 17:19:21 -07:00
Paul Frazee fd5bbb2769 Warn the user on links that dont match their text (#1573)
* Add link warning modal when URLs do not match their text

* Simplify the misleading link case for clarity

* Fix typecheck

* fix dark mode

* Give a stronger visual indication of the root domain in the link warning

* More rigorous URL mismatch logic

* Remove debug

---------

Co-authored-by: Ansh Nanda <anshnanda10@gmail.com>
2023-10-02 14:47:39 -07:00
Ansh 2f157c152a fix styling when on FeedsTab (#1574) 2023-10-02 13:28:18 -07:00
Paul Frazee 90b18cf004 Fix alt text selectability in lightbox on web (close #1548) (#1571) 2023-10-02 13:25:41 -07:00
Paul Frazee 64410efafe Small sizing tweaks to the web modal (#1568) 2023-10-02 09:28:04 -07:00
Manish 72e3763446 Add the build step for atproto (#1579)
Co-authored-by: Manish <manish.nayal@nium.com>
2023-10-02 09:18:34 -07:00
Ansh 04fda0f142 Fix 1405 handle android back button in composer (#1446)
* handle android back button in composer

* improve backHandler error handling

* simplify composer onClose functionality
2023-09-29 07:49:59 +04:00
Paul Frazee 1f60e1a748 Fix warning 2023-09-28 20:45:11 -07:00
Bryan Lee 2aae37d67b Improve Device Detection For Better Responsiveness (#1512)
* Refactor `useOnMainScroll` function to use responsive device detection

- Replace static `isDesktopWeb` with `useWebMediaQueries` hook to enable dynamic device type detection.
- Create `useDeviceLimits` hook to dynamically determine `DY_LIMIT_UP` and `DY_LIMIT_DOWN` based on device type.
- Update dependency arrays for the `useCallback` hooks to include new dynamic variables.

* Refactor styles to be responsive to device type

- Create `useStyles` hook that generates styles object based on device type detected from `useWebMediaQueries`.
- Replace static styles object with dynamic styles object generated from `useStyles` hook in components.
- This allows `paddingLeft` values for 'ul' and 'ol' styles to adapt to device type dynamically.
- This allows `maxWidth` values for 'metaItem'' styles to adapt to device type dynamically.

* Remove `isDesktopWeb` in favor of `useWebMediaQueries().isDesktop`

* Refactor `SplashScreen` component for responsive design

* Revision based on review results

* Fix isNative check

---------

Co-authored-by: Paul Frazee <pfrazee@gmail.com>
2023-09-28 12:47:34 -07:00
Paul Frazee 2e5f73ff61 Account quick switch modal (#1567)
* quick switch menu

* Some small tweaks and fixes to the account switch modal

* Factor out the account switcher logic to a hook

* Add haptic feedback on account switcher open

* Fix bad merge

---------

Co-authored-by: Samuel Newman <mozzius@protonmail.com>
2023-09-28 12:41:44 -07:00
Gabriel Donadel Dall'Agnol 3e340b336e Add example google-services file (#1545)
* Add example google-services file

* Update build.md
2023-09-28 12:22:23 -07:00
Paul Frazee 65b83e0d6b Merge branch 'Tanza3D-small-pfp-desktop-2' into main 2023-09-28 12:12:03 -07:00
Paul Frazee f5a78c2c86 Slightly larger desktop leftnav pfp 2023-09-28 12:11:09 -07:00
Paul Frazee d4a234d5ce Merge branch 'small-pfp-desktop-2' of https://github.com/Tanza3D/social-app into Tanza3D-small-pfp-desktop-2 2023-09-28 12:08:39 -07:00
Paul Frazee cd3b0e54fb Email verification and change flows (#1560)
* fix 'Reposted by' text overflow

* Add email verification flow

* Implement change email flow

* Add verify email reminder on load

* Bump @atproto/api@0.6.20

* Trim the inputs

* Accessibility fixes

* Fix typo

* Fix: include the day in the sharding check

* Update auto behaviors

* Update yarn.lock

* Temporary error message

---------

Co-authored-by: Eric Bailey <git@esb.lol>
2023-09-28 12:08:00 -07:00
Paul Frazee 16763d1d41 Handle long feed listings on the right nav with scrolling (#1562) 2023-09-28 10:33:14 -07:00
Paul Frazee cc1c9ab305 Merge branch 'mozzius-expand-author-pressable' into main 2023-09-28 10:32:55 -07:00
Paul Frazee d57c8f7bca Fix pointer 2023-09-28 10:32:34 -07:00
Paul Frazee 1aa1b69a94 Merge branch 'expand-author-pressable' of https://github.com/mozzius/bluesky-official-app into mozzius-expand-author-pressable 2023-09-28 10:27:24 -07:00
Paul Frazee e181bae9a6 Merge branch 'left-padding-on-sidebar' of https://github.com/Tanza3D/social-app into main 2023-09-28 10:25:38 -07:00
Tanza 83bb5c1710 Move sidebar by 10px for more even padding (#1543) 2023-09-28 10:24:46 -07:00
Samuel Newman a427010629 Don't render a pressable if there's only one notification author 2023-09-28 11:03:52 +01:00
Eric Bailey b030d94a64 fix link highlighting with mention present (#1544) 2023-09-27 09:15:50 -07:00
Eric Bailey 6d4ad59416 Merge pull request #1553 from mozzius/refreshed-web-repost-dropdown
Use refreshed dropdown style for repost/quote post dropdown
2023-09-27 11:14:56 -05:00
Paul Frazee d87c232660 Improve image cropping on android and introduce aspect ratio field (#1525)
* Fix image cropping on android

* Store and use aspect ratio field in post images (close #1392)
2023-09-27 09:08:21 -07:00
Eric Bailey 6325eff938 Improvements to auto-mentioning users from their profiles (#1556)
* Don't automatically mention users with invalid handles

* don't mention when using did urls

* resolve profile from cache

* a little clearer

---------

Co-authored-by: Samuel Newman <mozzius@protonmail.com>
2023-09-27 09:06:33 -07:00
Samuel Newman 5d59ab2ac0 replace dropdown component 2023-09-27 11:32:42 +01:00
Tanza3D e941da646d Move sidebar by 10px for more even padding 2023-09-26 22:12:22 +01:00
Tanza3D 1185ac4053 Small PFP on desktop 2023-09-26 22:07:56 +01:00
Eric Bailey 42723dfaf6 fix 'Reposted by' text overflow (#1535) 2023-09-26 12:24:05 -07:00
dan 95736a5168 Add instructions for running on device (#1541) 2023-09-26 11:46:46 -07:00
Foysal Ahamed b0873cae97 💄 Remove report option on my own post (#1537) 2023-09-26 10:48:07 -07:00
Paul Frazee 83b3b5e1c0 Merge branch 'main' of github.com:bluesky-social/social-app into main 2023-09-25 13:50:44 -07:00
Sasha Mansurov 5f476851a3 Fix gallery image size (#1474)
Fixes #1323
2023-09-25 13:50:29 -07:00
Paul Frazee be2f319a34 Bump android version code 2023-09-22 18:13:07 -07:00
Paul Frazee e9f30894b1 Bump ios build number 2023-09-22 18:08:50 -07:00
Paul Frazee 150fe421d6 Fixes to tests (#1518) 2023-09-22 17:49:41 -07:00
Paul Frazee 41b53ca37b Trim whitespace on invite code (#1517) 2023-09-22 17:04:58 -07:00
Paul Frazee b7697f08d6 Accessibility fixes: add missing labels, dynamically scale home header (#1516)
* Improve labels

* Dynanically adjust home header to account for font scaling
2023-09-22 17:04:47 -07:00
Paul Frazee 88b95df200 Update backend routes to match frontend (#1515) 2023-09-22 17:04:36 -07:00
Eric Bailey 1b3b4c6692 Merge pull request #1513 from bluesky-social/eric/app-855-update-language-settings-in-app
update translator link show logic
2023-09-22 10:22:28 -05:00
Eric Bailey 28f5a8e44d bool 2023-09-22 09:46:46 -05:00
Eric Bailey 4fa576cb30 update translator link show logic 2023-09-22 09:24:48 -05:00
Paul Frazee 146132a080 Bump ios buildnumber 2023-09-21 21:01:15 -07:00
Paul Frazee 8584009bae Move home feed and thread preferences to server (#1507)
* Move home feed and thread preferences to server

* Fix thread usage of prefs

* Remove log

* Bump @atproto/api@0.6.16

* Improve type usage
2023-09-21 21:00:32 -07:00
Paul Frazee 28b692a118 Switch the date input web version to the date-input web control (#1510)
* Switch the date input web version to the date-input web control

* Fix types
2023-09-21 17:41:09 -07:00
Paul Frazee 1abe5270dd Wrap the post thread screen in an observer (#1509) 2023-09-21 17:36:50 -07:00
Paul Frazee 44985d2312 Dont reposition load latest button on desktop (#1508) 2023-09-21 17:36:33 -07:00
Paul Frazee 8aabbb9b6f Minor alignment fix 2023-09-21 15:07:56 -07:00
Paul Frazee 7931787dd7 Tune the treeview mode to use larger immediate-children (#1506)
* Tune the treeview mode to use larger immediate-children

* Fix display name alignment
2023-09-21 13:47:42 -07:00
Eric Bailey 8a5f9cd43d Language settings updates, new primary language setting (#1471)
* move content languages to screen

* add dropdown library, style primary lang select

* update settings button

* show selected langauges in button

* use primary language in translator link

* update copy

* lint
2023-09-21 11:33:19 -07:00
Paul Frazee 335061f763 Bump ios build number 2023-09-20 19:48:40 -07:00
Eric Bailey e837a499f3 add suggested follows metrics (#1503) 2023-09-20 19:48:03 -07:00
Paul Frazee 5a945c2024 Prefilter the mergefeed to ensure a better mix of following and custom feeds (#1498)
* Prefilter the mergefeed to ensure a better mix of following and custom feeds

* Test suite improvements & tests for the mergefeed (#1499)

* Disable invite codes test for now

* Update test sim to latest iphone

* Introduce TestCtrls driver

* Add mergefeed tests
2023-09-20 19:47:56 -07:00
Paul Frazee 68dd3210d1 Improve consistency of floating button positioning (#1501) 2023-09-20 19:45:05 -07:00
Bryan Lee f5e9e4af89 Updates InviteCodes modal to use dynamic/responsive styles on web (#1502) 2023-09-20 19:16:33 -07:00
Eric Bailey 6df1bcad31 add suggested follow section to profile header (#1481)
* add suggested follow section to profile header

* fix button overflow

* don't even render on preview

* fix useFollowDid and FollowButton race condition

* add section header, close button, active state

* lighten icon
2023-09-20 19:16:11 -07:00
Paul Frazee 498c3e2c27 Tune the treeview UI a bit more (#1494) 2023-09-20 19:13:54 -07:00
Paul Frazee acc98ac1ab Dont show the reply prompt if minimal shell mode triggers (#1495) 2023-09-20 19:12:40 -07:00
Paul Frazee 95e8a6bf41 Even less intrusive status line for posts (#1496) 2023-09-20 19:11:28 -07:00
Paul Frazee 8f8bed9a1c Fix a mobx complaint about mutations (#1500) 2023-09-20 19:11:07 -07:00
dan d33aaf57ad Add a tip about Android emulator memory limit (#1492) 2023-09-20 12:03:07 -07:00
Eric Bailey a1c040cd79 Merge pull request #1476 from bluesky-social/bnewbold/bump-api-dep
yarn: bump @atproto/api to remove redundant deps
2023-09-20 11:07:13 -05:00
Eric Bailey 5665968f72 Merge remote-tracking branch 'origin' into bnewbold/bump-api-dep
* origin:
  Allow touch at the top of the lightbox (#1489)
  Bump ios build number
  Feeds tab fixes (#1486)
  Nicer 'post processing status' in the composer (#1472)
  Inline createPanResponder (#1483)
  Tree view threads experiment (#1480)
  Make "double tap to zoom" precise across platforms (#1482)
  Onboarding recommended follows (#1457)
  Add thread sort settings (#1475)
2023-09-20 11:03:57 -05:00
dan cd96f8dcc8 Allow touch at the top of the lightbox (#1489) 2023-09-20 12:41:18 +01:00
Paul Frazee 6db7113108 Bump ios build number 2023-09-19 21:45:03 -07:00
Paul Frazee 971c8025e6 Feeds tab fixes (#1486)
* Bold the saved feeds on mobile

* Improve the saved feeds loading state

* Add soft reset handler to feeds page

* Show feed descriptions in profile listing

* Add an 'about this feed' modal

* Fix type assertion
2023-09-19 21:44:23 -07:00
Paul Frazee 753fb8bfbc Nicer 'post processing status' in the composer (#1472) 2023-09-19 20:40:58 -07:00
dan 8b8fba7284 Inline createPanResponder (#1483) 2023-09-20 03:46:59 +01:00
Paul Frazee 1af8e83d53 Tree view threads experiment (#1480)
* Add tree-view experiment to threads

* Fix typo

* Remove extra minimalshellmode call

* Fix to parent line rendering

* Fix extra border

* Some ui cleanup
2023-09-19 19:08:11 -07:00
dan d2c253a284 Make "double tap to zoom" precise across platforms (#1482)
* Implement double tap for Android

* Match the new behavior on iOS
2023-09-19 18:32:44 -07:00
Ansh 859588c3f6 Onboarding recommended follows (#1457)
* upgrade api package

* add RecommendedFollows as a step in onboarding

* add list of recommended follows from suggested actor model

* remove dead code

* hoist suggestedActors into onboarding model

* add comments

* load more suggested follows on follow

* styling changes

* add animation

* tweak animations

* adjust styling slightly

* adjust styles on mobile

* styling improvements for web

* fix text alignment in RecommendedFollows

* dedupe inserted suggestions

* fix animation duration

* Minor spacing tweak

---------

Co-authored-by: Paul Frazee <pfrazee@gmail.com> and Eric Bailey <git@esb.lol>
2023-09-20 01:18:50 +05:30
Paul Frazee da8499c881 Add thread sort settings (#1475)
* Add thread sorting preferences

* UI tweaks

* Tweak settings

* Tune the copy
2023-09-19 12:24:58 -07:00
bryan newbold 63527493fd yarn: bump @atproto/api to remove redundant deps 2023-09-18 18:39:26 -07:00
Paul Frazee 9c4374f66a 1.51 2023-09-18 14:34:41 -07:00
Foysal Ahamed 3c4899b3c4 🐛 Handle middle mouse click on feed list items (#1469)
* 🐛 Handle middle mouse click on feed list items

* ♻️ Refactor the event listener and turn it into a dedicated hook for web

* 🧹 Cleanup unnecessary Link changes

* Fix import

* Create native version of useAuxClick

---------

Co-authored-by: Paul Frazee <pfrazee@gmail.com>
2023-09-18 14:34:12 -07:00
Paul Frazee 255beb0c1f Various release fixes (#1470)
* Fix error state for recommended feeds

* Handle failure to set default feeds more safely

* Dont return undefined within react-query

* Update yarn.lock
2023-09-18 13:38:33 -07:00
Paul Frazee 157daad605 Update yarn.lock 2023-09-18 12:05:18 -07:00
Paul Frazee ea885339cf Feed UI update working branch [WIP] (#1420)
* Feeds navigation on right side of desktop (#1403)

* Remove home feed header on desktop

* Add feeds to right sidebar

* Add simple non-moving header to desktop

* Improve loading state of custom feed header

* Remove log

Co-authored-by: Eric Bailey <git@esb.lol>

* Remove dead comment

---------

Co-authored-by: Eric Bailey <git@esb.lol>

* Redesign feeds tab (#1439)

* consolidate saved feeds and discover into one screen

* Add hoverStyle behavior to <Link>

* More UI work on SavedFeeds

* Replace satellite icon with a hashtag

* Tune My Feeds mobile ui

* Handle no results in my feeds

* Remove old DiscoverFeeds screen

* Remove multifeed

* Remove DiscoverFeeds from router

* Improve loading placeholders

* Small fixes

* Fix types

* Fix overflow issue on firefox

* Add icons prompting to open feeds

---------

Co-authored-by: Paul Frazee <pfrazee@gmail.com>

* Merge feed prototype [WIP] (#1398)

* POC WIP for the mergefeed

* Add feed API wrapper and move mergefeed into it

* Show feed source in mergefeed

* Add lodash.random dep

* Improve mergefeed sampling and reliability

* Tune source ui element

* Improve mergefeed edge condition handling

* Remove in-place update of feeds for performance

* Fix link on native

* Fix bad ref

* Improve variety in mergefeed sampling

* Fix types

* Fix rebase error

* Add missing source field (got dropped in merge)

* Update find more link

* Simplify the right hand feeds nav

* Bring back load latest button on desktop & unify impl

* Add 'From' to source

* Add simple headers to desktop home & notifications

* Fix thread view jumping around horizontally

* Add unread indicators to desktop headers

* Add home feed preference for enabling the mergefeed

* Add a preference for showing replies among followed users only (#1448)

* Add a preference for showing replies among followed users only

* Simplify the reply filter UI

* Fix typo

* Simplified custom feed header

* Add soft reset to custom feed screen

* Drop all the in-post translate links except when expanded (#1455)

* Update mobile feed settings links to match desktop

* Fixes to feeds screen loading states

* Bolder active state of feeds tab on mobile web

* Fix dark mode issue

---------

Co-authored-by: Eric Bailey <git@esb.lol>
Co-authored-by: Ansh <anshnanda10@gmail.com>
2023-09-18 11:44:29 -07:00
Eric Bailey 3118e3e933 improve drawer reflow for large text (#1464) 2023-09-15 15:18:09 -07:00
Paul Frazee 754663e5c3 Add findlast shim for outdated browsers (#1463)
* Add findlast shim for crappy browsers

* Fix types
2023-09-15 14:40:15 -07:00
Paul Frazee 0672451ddc Sanitize BIDI characters from display names (#1462) 2023-09-15 14:05:45 -07:00
Paul Frazee 763a50692f Readd hitslop to post controls (#1461)
* Readd hitslop to post controls

* Bigger hitslop on bigger controls
2023-09-15 14:01:44 -07:00
Eric Bailey e3de91788e Merge pull request #1452 from bluesky-social/eric/update-image-picker
bump picker deps
2023-09-15 14:06:55 -05:00
Eric Bailey 8593f41f36 don't thread a self-repost of a self-reply (#1450)
* don't thread a self-repost of a self-reply

* typo
2023-09-15 09:34:12 -07:00
Ansh 188d4893f9 add React Query and hook up to existing functionality (#1358)
* add React Query and hook up to existing functionality

* wire in remote data, add error message

* remove hard-coded feeds

* oops fix logic

* add loading state

* fix loading on mobile

---------

Co-authored-by: Eric Bailey <git@esb.lol>
2023-09-15 09:32:44 -07:00
Eric Bailey 84b7edd9db Merge pull request #1451 from bluesky-social/eric/theme-switching-web
fix theme switching on web without refresh
2023-09-15 11:06:06 -05:00
Eric Bailey 0e8d564555 link out to more context 2023-09-15 11:00:06 -05:00
Eric Bailey 01870883d6 clean up into hook 2023-09-14 18:15:17 -05:00
Ansh 50f811666a Fix Profile link from drawer on Mobile Web (#1437)
* 🗺️ fix an issue where web devices that see the Drawer couldn't access Profile from said Drawer

* fix routes on web

* handle profile button active state

* add hack for web navigation

* fix comment

---------

Co-authored-by: Micah Maligie <kerosuppi@gmail.com>
2023-09-14 16:10:59 -07:00
Eric Bailey e643c43459 add build:apk and apk config (#1454) 2023-09-14 16:06:25 -07:00
bnewbold 37252e90b4 update Bluesky PBLLC to PBC (Public Benefit Corporation) (#1453) 2023-09-14 14:51:06 -07:00
Eric Bailey bd8dac0154 bump picker deps 2023-09-14 15:17:57 -05:00
Eric Bailey 9a86f0dae4 simplify logic 2023-09-14 13:23:20 -05:00
Eric Bailey a88ac217c2 fix theme switching on web without refresh 2023-09-14 12:55:47 -05:00
Ansh b9268d503a wrap impersonation text (#1447) 2023-09-13 11:12:58 -07:00
Ansh 9c72fcc9b4 share mute list link with did not handle (#1438) 2023-09-12 15:35:34 -07:00
Ansh 4977a5d2a3 Fix 651 Make followers and following button into a <Link> (#1429)
* make followers and following button into a Link

* make buttons into `<a>` tags
2023-09-12 13:47:55 -07:00
Paul Frazee 2b6fe4cb50 1.50 2023-09-11 18:23:40 -07:00
Paul Frazee 9b5bd53f84 Remove accessibilityRole=button on dropdown mask, it causes issues on firefox (#1436) 2023-09-11 18:09:02 -07:00
Paul Frazee 9e8b14f710 Add birth date gating to moderation settings (#1435)
* Add birth date preference, modal to set, link in settings, and age gate in moderation

* Styling fixes for android

* Fix types
2023-09-11 18:04:09 -07:00
Paul Frazee 0090371011 Paginate the PostThread to avoid rendering too many posts and crashing the app on large threads (#1432) 2023-09-11 17:41:00 -07:00
Eric Bailey 21371081c6 fix theme flashing (#1417)
* fix theme flashing

* remove memo

* use AppState listener
2023-09-11 14:39:31 -07:00
Paul Frazee f8c611118e Separate list deletion into chunks (close #1430) (#1431) 2023-09-11 14:28:30 -07:00
Paul Frazee 977f9228f8 Revert "Fix 1338 mark notifications read with timeout and on app state change (#1340)" (#1433)
This reverts commit d2e65d2a7f.
2023-09-11 14:28:13 -07:00
Paul Frazee 57c10cbcab Bump @atproto/api@0.6.12 (#1434) 2023-09-11 14:28:02 -07:00
Ansh 27cb1bd276 make sure click events on the emoji picker don't bubble up to the mask (#1427) 2023-09-11 13:31:14 -07:00
Ansh bcd63e710c Lytic analytics migration (#1428)
* add new api event proxy

* add new apiHost for analytics web
2023-09-11 11:41:27 -07:00
Ansh 01775e5bff add new api event proxy (#1400) 2023-09-11 22:36:49 +05:30
uakci 731d84faaa composer: inhibit Cmd-Enter from inputting newline (#1421) 2023-09-11 09:02:14 -07:00
Bossett 775aa87540 Update time.ts to handle very small or negative time differences (#1390)
* Update time.ts to handle very small or negative time differences

Right now, posts can appear to be from the future with a negative time difference (i.e. -3s appears). This change defines 'NOW' as less than 5 seconds old, and returns 'now' in that case.

It's not clear how localisation is handled - this may need translation.

* Add test for 'now' in time/ago(...)

Add tests for ago() for right now (i.e. 'now') and 10s ago to ensure the seconds case is still tested
2023-09-08 08:57:22 -07:00
dan 6d73ed96e1 Make Sentry optional (#1416)
* Move app.json -> app.config.js

* Make Sentry optional
2023-09-08 08:47:01 -07:00
dan adc5b75b2b Fix autocomplete avatar alignment (#1414) 2023-09-08 16:30:42 +01:00
dan 44a00e1b7e Set crop mode to None by default (#1415)
Fixes #1263

Previously, opening the crop tool had 1:1 selected by default. So if you opened it and pressed Done without changing anything, it would format the image as 1:1.

After this change, None is selected by default. So if you open the crop tool and press Done without changing anything, nothing will change.
2023-09-08 16:30:00 +01:00
Micah Maligie c10cb8e972 💅 disallow overflow outside of post content area(s) (#1395) 2023-09-07 18:13:28 -07:00
dan 8a93321fb1 Give explicit names to MobX observer components (#1413)
* Consider observer(...) as components

* Add display names to MobX observers

* Temporarily suppress nested components

* Suppress new false positives for react/prop-types
2023-09-07 17:36:08 -07:00
dan 69209c988f Update build instructions (#1406)
- Use pnpm
- Clarify when prebuild is needed
2023-09-07 17:29:16 -07:00
dan a5b89dffa6 Add ESLint React plugin (#1412)
* Add eslint-plugin-react

* Enable display name rule
2023-09-07 16:38:57 -07:00
Ansh 00595591c4 Hotfix: Fix unable to enter invite code (#1409)
* Remove accessibilityRole from invite code input

* remove unused styles
2023-09-07 13:50:24 -07:00
Paul Frazee 2963fb4c6d Fix scrollbar gutters on web (#1401)
* Fix scrollbar gutters on web

* Fix typecheck
2023-09-07 09:32:14 -07:00
dan 138e6d9b97 Fix lint-staged not allowing edits to Markdown files (#1402)
* Remove unnecessary overrides config

* Only lint-staged code files
2023-09-07 02:06:07 +01:00
dan 82a17db594 Always prebuild cleanly (#1399) 2023-09-06 17:21:48 -07:00
Eric Bailey b76b56b4a4 Merge pull request #1397 from bluesky-social/eric/fix-startup-error
prevent error screen on startup
2023-09-06 16:33:56 -05:00
Eric Bailey fdc4ae1f7b prevent error screen on startup 2023-09-06 16:27:44 -05:00
Minseo Lee b6d239aa37 Update ProfileHeader.tsx (#1389) 2023-09-06 13:45:50 -07:00
Ansh 63f0b2f0e5 fix text color on waitlist join (#1387) 2023-09-06 09:37:07 -07:00
dan dd5697329f Remove unnecessary deps from package.json (#1393) 2023-09-06 09:36:45 -07:00
dan 19adb10de1 Use frozen lockfile for CI workflows (#1394) 2023-09-06 09:26:19 -07:00
Paul Frazee 565c1a0510 1.49 2023-09-05 14:04:36 -07:00
Paul Frazee 52f3810e81 Update tests 2023-09-05 14:03:39 -07:00
Paul Frazee 411c1581f6 Fix thread positioning on load (#1385)
* Fix minIndex for consistent positioning on thread view

* Enable maintainVisibleContentPosition on android now that it's supported
2023-09-05 13:40:38 -07:00
Eric Bailey e3e91816d1 Upgrade to Expo 49 (#1331)
* update to expo 49

* update expo-camera to fix console erorrs

* run doctor again

* fix ts errors

* patch @sentry/react-native

Getting `cannot read property 'ignoreLogs' of undefined` in this file.
Ironically, this may be a cyclical imports problem. LogBox isn't enabled
in production, so this patch should only affect dev/test.

* fix type error

* reinstall newer reanimated

* pin expo-dev-client to fix reanimated dev-build issue

* fix type errors, fix bad conflict res

* Fix to notifications badge z-index on desktop

---------

Co-authored-by: Paul Frazee <pfrazee@gmail.com>
2023-09-05 12:23:22 -07:00
dan 9a3fa512eb Enable Fast Refresh for web (#1383) 2023-09-05 11:13:42 -07:00
Paul Frazee 764c7cd569 Updates to use dynamic/responsive styles on web (#1351)
* Move most responsive queries to the hook

* Fix invalid CSS value

* Fixes to tablet render of post thread

* Fix overflow issues on web

* Fix search header on tablet

* Fix QP margin in web composer

* Fix: only apply double gutter once to flatlist (close #1368)

* Fix styles on discover feeds header

* Fix double discover links in multifeed
2023-09-05 10:42:19 -07:00
dan be8084ae10 Fix mismatching button label for speech recognition (#1372)
* Fix mismatching a11y label

* Update all new post button labels to match
2023-09-05 16:55:39 +01:00
dan 161746519c Fix caret jumps for web composer (#1374)
* Fix caret jumps for web composer

* Autofocus at the end on mount
2023-09-05 08:21:10 +01:00
Paul Frazee 419ac2d0df Improved mute alerting (#1355)
* Use a simpler mute alert when viewing a post by a muted user

* Dont mute self-QPs when we're overriding a mute

* Fix types
2023-09-01 12:30:02 -07:00
Paul Frazee 3e96373903 Sort thread replies by likes (#1356)
* Sort replies by likes

* Types fix
2023-09-01 11:54:51 -07:00
Ansh 4cd3ddecad refresh when empty (#1354) 2023-08-31 16:45:18 -07:00
Samuel Newman f16cc9537a show images in notification if post also has record embed (#1349) 2023-08-31 16:44:42 -07:00
Eric Bailey ba393cd642 encode all unsafe characters in post text for translate URL (#1345) 2023-08-31 16:39:18 -07:00
Ansh d2e65d2a7f Fix 1338 mark notifications read with timeout and on app state change (#1340)
* mark notifications read with timeout and on app state change

* fix type
2023-08-30 18:06:02 -07:00
Ansh 4ac82536c9 Fix 1304 refresh notifications when a notif is clicked or received (#1339)
* refresh notifications when a notif is clicked

* make notification syncing smarter

* allow enabled appview proxy on mobile

* put back syncqueue code
2023-08-30 18:04:33 -07:00
Ansh 4bec7c1d85 Fix #1328 fix emoji picker scroll on firefox and chrome (#1332)
* fix emoji picker scroll on firefox and chrome

* click outside emoji picker to dismiss

* dynamically position emoji picker

* dynamically placing picker
2023-08-30 17:58:24 -07:00
Paul Frazee 04992f14f1 Improvements to UI in web logged-out views (#1341)
* Add LoggedOutLayout for desktop/tablet web

* Avoid screen flash in the transition to onboarding

* Fix comment
2023-08-30 17:55:01 -07:00
Ansh a498acab6e fix types for breakpoints on mobile (#1342) 2023-08-30 17:02:23 -07:00
Paul Frazee f9cab178b9 Merge branch 'ansh/app-812-add-custom-feed-discovery-to-onboarding' into main 2023-08-30 16:18:21 -07:00
Paul Frazee 59dcedeea2 Fix: remove bottom bar during onboard 2023-08-30 16:15:06 -07:00
Paul Frazee 8bc8dcc094 Simplify the RecommendedFeeds with breakpoint components 2023-08-30 16:13:09 -07:00
Paul Frazee 3fa9b6daba Fix types and remove dead code 2023-08-30 15:30:26 -07:00
Ansh 9446c67880 Move PreferencesHomeFeed to a screen instead of a modal (#1335)
* move `PreferencesHomeFeed` to a screen instead of a modal

* add web route for home feed preferences

* upgrade `@miblanchard/react-native-slider` to fix lint

* fix web route naming

* fix desktop web styling

* add `react-native-slider` mock
2023-08-30 15:21:12 -07:00
Paul Frazee a29f10aefe Moderation settings fixes (#1336)
* Default isAdultContentEnabled to false on all devices.

The original intent of setting the default based on the device was
to make the maximally-permissive choice. It turns out this was a
mistake as it created sync issues between devices; users would be
confused by the lack of congruity between them. We have to go with
false by default to ensure sync is retained.

* Update preferences model to use new sdk api

* Delete dead code

* Dont show the iOS adult content warning in content filtering settings if adult content is enabled

* Bump @atproto/api@0.6.8

* Codebase style consistency
2023-08-30 15:19:19 -07:00
Paul Frazee 8ed6e72ea4 More fixes to mobile onboard 2023-08-30 15:15:10 -07:00
Paul Frazee 05d1d8d8a4 Fix onboarding on mobile web 2023-08-30 14:57:03 -07:00
Paul Frazee cd8ae1298e Tweak dark mode on web 2023-08-30 10:56:07 -07:00
Paul Frazee b847917969 Tweaks to mobile onboard 2023-08-29 23:02:37 -07:00
Paul Frazee 5e765bf1cb Rework web onboarding 2023-08-29 22:56:13 -07:00
Paul Frazee 5d9534ca72 Move onboarding to the withAuthRequired HOC 2023-08-29 20:20:51 -07:00
Eric Bailey 3a90b479fd fix lightbox on search page (#1330)
* fix lightbox on search page

* add styles to mobile web too
2023-08-29 17:41:50 -07:00
Eric Bailey 22b7964b5e Merge pull request #1334 from bluesky-social/eric/fix-android-perms-noop
handle stuck state with granular android permissions
2023-08-29 19:27:43 -05:00
Eric Bailey 5710140a73 handle stuck state with granular android permissions 2023-08-29 17:09:38 -05:00
Ansh Nanda bf37913701 fix onboarding on web 2023-08-29 12:16:26 -07:00
Eric Bailey 6bee7d518b bump yarn (#1329) 2023-08-29 11:01:14 -07:00
bnewbold 461c1da6de update golang to v1.21; set GOEXPERIMENT=loopvar (#1256)
* golang: update to v1.21, and set GOEXPERIMENT=loopvar

* golang: go mod tidy
2023-08-28 19:35:23 -07:00
Paul Frazee 40e030793a 1.48 2023-08-28 19:11:15 -07:00
Paul Frazee 2619c55c10 Test fixes 2023-08-28 19:10:36 -07:00
Ansh Nanda 742440c22d add more tracking 2023-08-28 18:55:40 -07:00
Ansh Nanda 8f0f3be8fb start onboarding only for new account creations 2023-08-28 18:53:58 -07:00
Ansh Nanda bcef7400db simplify onboarding model function naming 2023-08-28 18:47:56 -07:00
Eric Bailey 1c460c40f4 Make posts behave more like links (#1316)
* use cursor for post cards

* ignore type error

* handle meta keys on non native links

(cherry picked from commit daccafea0b7ab21af6572767e496d20f32ead353)

* remove cursor on non-post notifications, not quite right

* Simplify link handling

---------

Co-authored-by: Paul Frazee <pfrazee@gmail.com>
2023-08-28 18:41:28 -07:00
Paul Frazee cc2838761b Replace web editor link behavior (#1319)
* Replace web editor link behavior (close #1293) (close #1292)

* Update link decorator to match rich text link detector
2023-08-28 18:40:45 -07:00
Patroll 2c60a0328d Fix: Don't flash default profile avatar and link in nav (#1287) 2023-08-28 17:58:14 -07:00
Paul Frazee 5ee754e6f9 Improvements to feed assembly to avoid possible state issues (#1318)
* Avoid potential dropped posts due to pruning when checking for latest

* Add a sanity check to ensure dup react keys never occur (close #1315)
2023-08-28 17:54:59 -07:00
Ansh Nanda a231fdf64e track onboarding steps 2023-08-28 17:51:26 -07:00
Ansh Nanda b38629b9b5 Merge branch 'main' into ansh/app-812-add-custom-feed-discovery-to-onboarding 2023-08-28 17:49:14 -07:00
Paul Frazee e2f0770b88 Remove ability for Post component to load from URI (close #1302) (#1317) 2023-08-28 17:08:21 -07:00
Ansh Nanda b2dd4ea0f5 fix onboarding stuck on welcome 2023-08-28 15:50:51 -07:00
Ansh Nanda 3f1b313fa4 add custom feed discovery to onboarding 2023-08-28 15:41:02 -07:00
Ansh Nanda c4cf288296 remove logs 2023-08-28 13:39:24 -07:00
Ansh Nanda edfd326069 move onboarding to screens 2023-08-28 13:37:44 -07:00
Ansh Nanda 84e065667a update packages 2023-08-28 13:36:34 -07:00
Eric Bailey c77fd588e9 wrap mutation in runInAction (#1305) 2023-08-28 11:55:56 -07:00
Ansh Nanda 0ca28ccca6 Merge branch 'main' into ansh/app-812-add-custom-feed-discovery-to-onboarding 2023-08-25 14:53:03 -07:00
Ansh Nanda 08fe7fb084 create onboarding model 2023-08-25 14:36:10 -07:00
Paul Frazee a3cb12f584 Move the web composer to the top of the screen (#1282) 2023-08-25 10:30:15 -07:00
Eric Bailey b15a435069 Eslint updates (#1281)
* eslint: Update eslintrc

* eslint: Strings must use singlequote  quotes

* eslint: expected { after if-condition

* eslint: update warnings

* a little cleanup

* remove conflicted file

---------

Co-authored-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
Co-authored-by: Paul Frazee <pfrazee@gmail.com>
2023-08-25 08:48:38 -07:00
Eric Bailey a0dca81a74 don't mention own user from composer (#1279) 2023-08-25 08:47:47 -07:00
Eric Bailey 953ae9c096 Update/fix ChangeHandle form (#1280)
* fix well-known did copy value

* s/Domain/Host in change handle form
2023-08-25 08:47:12 -07:00
Paul Frazee 37a70f0a67 1.47 build 3 2023-08-24 16:29:44 -07:00
Eric Bailey 462022741d Use Pressable for most links/embeds (#1181)
* delay press on all links

* use Pressable for all accessible links
2023-08-24 16:28:26 -07:00
Eric Bailey 4654a9a45e defer loading of feeds until visible (#1271)
* defer loading of feeds until visible

* Fix: use existing hasLoaded

* Fix: dont query for latest during initial load

---------

Co-authored-by: Paul Frazee <pfrazee@gmail.com>
2023-08-24 16:26:29 -07:00
Paul Frazee a5981e127f Fixes to the feed discovery page (#1238)
* Bump yarn.lock for... reasons

* Restyle DiscoverFeeds empty state (close #1236)

* Fix: maintain search state in feed discovery (close #1204)

* tsconfig fixes

* Tweaks to yarn.lock
2023-08-24 16:16:14 -07:00
Ansh 7410bcd888 setup isAuthenticatedUser in profileUiModel inside an action (#1272) 2023-08-24 16:13:42 -07:00
Paul Frazee c6f321862b Add analytics event for when the app is opened from a notification (#1270)
* Add analytics event for when the app is opened from a notification

* Tweak notification ID
2023-08-24 16:07:16 -07:00
bnewbold a115c17a09 bskyweb: switch from PDS backend to AppView backend (#1065)
* bskyweb: replace PDS with AppView (no auth)

* bskyweb: update example .env files for appview
2023-08-24 16:00:45 -07:00
Paul Frazee 766521486c Increment build number 2023-08-23 17:14:30 -07:00
Paul Frazee c1c02f480b A couple quick notifications fixes (#1262)
* Create separate .easignore to ensure google-services.json goes to eas

* Set the notifications serviceDid according to the session host

* Fix types
2023-08-23 17:10:00 -07:00
Paul Frazee 93d5ba1820 Fix eas.json 2023-08-23 16:35:01 -07:00
Paul Frazee 9bd9d40306 1.47 2023-08-23 16:30:33 -07:00
Ansh 6487a87542 [APP-836] Emoji picker for web (#1254)
* add emoji-mart package for emoji dropdown picker

* remove emoji picker modal

* load emoji mart data not as part of the main bundle

* remove @emoji-mart/data

* setup emoji insertion with events

* get emoji data from local static assets

* close emoji picker after one emoji has been inserted

* Switch emoji picker trigger to an icon

* Update emoji-mart-data.js

* make grabbing emoji data work on more browsers

---------

Co-authored-by: Paul Frazee <pfrazee@gmail.com>
2023-08-23 16:29:23 -07:00
Ansh 8ab5eb6583 [APP-786] Native notifications (#1095)
* move `notifee.ts` to notifications folder

* install expo notifications

* add UIBackgroundMode `remote-notifications` to app.json

* fix notifee import in Debug.tsx

* add `google-services.json`

* add `development-device` class to eas.json

* Add `notifications.ts` for native notification handling

* send push token to server

* update `@atproto/api`

* fix putting notif token to server

* fix how push token is uploaded

* fix lint

* enable debug appview proxy header on all platforms

* setup `notifications.ts` to work with app view notifs

* clean up notification handler

* add comments

* update packages to correct versions

* remove notifee

* clean up code a lil

* rename push token endpoint

* remove unnecessary comments

* fix comments

* Remove old background scheduler

* Fixes to push notifications API use

* Bump @atproto/api@0.6.6

---------

Co-authored-by: Paul Frazee <pfrazee@gmail.com>
2023-08-23 16:28:51 -07:00
Ansh 32b9648931 Fixes #1252 fix alt text setting description toggle (#1261)
* make toggle border width slightly larger

* change copy
2023-08-23 16:16:55 -07:00
Eric Bailey b6317d4ce7 Improve handling of unselecting languanges in composer language menu (#1093)
* allow toggling off/on multiple from main composer lang menu

* fix dropdown styles for long labels

* udpate model to use new string field

* update language UI

* save langs to history on submit

* remove edit

* clean up use new fields

* default to deviceLocales

* fix default valu

* feedback

* use radio icon
2023-08-23 13:40:15 -07:00
Eric Bailey acad8cb455 Merge pull request #1260 from bluesky-social/eric/fix-window
fix bad reference on native
2023-08-23 14:34:41 -05:00
Eric Bailey 473a9fc73e fix bad reference on native 2023-08-23 14:31:08 -05:00
Jake Gold b4931fa185 bskyweb: cache /static/{images,js} for 1 week, other files for 1 hour 2023-08-23 09:23:09 -07:00
Paul Frazee 8e1d0b0396 Change Handle modal DNS instruction tweaks for clarity (#1245)
* Remove potentially confusing period from subdomain in changehandle (close #1004)

* Clarify dns instructions further (close #1087)
2023-08-22 16:37:04 -07:00
Paul Frazee 54706a0437 Fix: update posts rendered in notifs on refresh (close #1196) (#1243) 2023-08-22 16:36:49 -07:00
Paul Frazee fcea27ea0b Fix: include link-meta abort signal for timeout (close #1214) (#1240) 2023-08-22 16:36:36 -07:00
Paul Frazee 54118fe478 Fix feed empty state (close #1235) (#1239) 2023-08-22 16:36:23 -07:00
Eric Bailey 2b13dc0f3a Add likes tab (#1227)
* add likes tab

(cherry picked from commit 6c5ffd964ca0b185ddfc37088d82712a006a1163)

* only show Likes tab to authenticated user

(cherry picked from commit ecc1254411d760158b6d7a4c5f05d940db872dfc)

* Bump @atproto/api@0.6.5

* fix types

---------

Co-authored-by: Paul Frazee <pfrazee@gmail.com>
2023-08-22 12:30:51 -07:00
Eric Bailey 548ec6c82d add title attr to text text links (#1228)
* add title attr to text text links

* Revert "add title attr to text text links"

This reverts commit c028cd184e.

* use css tooltips

* add to expanded post state

* handle theming

* add to bskyweb
2023-08-22 11:04:17 -07:00
Ansh 16b265a861 [APP-834] Allow @ing someone in post directly from profile (#1241)
* setup `initMention` for mobile

* setup creating post with profile tagged on web
2023-08-22 11:01:00 -07:00
Ansh 3aadc43c89 [APP-835] trim end of email during login (#1242)
* trim end of email during login

* Switch trimEnd() to trim()

---------

Co-authored-by: Paul Frazee <pfrazee@gmail.com>
2023-08-22 10:52:59 -07:00
Paul Frazee fb1199e21d Merge branch 'main' of github.com:bluesky-social/social-app into main 2023-08-22 10:50:45 -07:00
Paul Frazee 0a50df1cce Minor formatting 2023-08-22 10:50:19 -07:00
Paul Frazee 17fcc2f25c Merge branch 'fix-empty-search' of https://github.com/darnfish/social-app into darnfish-fix-empty-search 2023-08-22 10:49:53 -07:00
Eric Bailey 13c11801b9 fix thread double border (#1251) 2023-08-22 10:49:03 -07:00
Eric Bailey 09a445d804 do not thread author media filtered feed (#1253) 2023-08-22 10:46:49 -07:00
William Gibson d3f525ab28 Don't submit search if query is empty 2023-08-22 04:27:46 -04:00
Ansh 88357d5c82 add profiles sampling to sentry (#1237) 2023-08-21 16:49:59 -07:00
Eric Bailey 4cc2695d84 allow handles to start with a number (#1231)
* allow handles to start with a number

* don't allow leading hyphen
2023-08-21 13:37:57 -07:00
Paul Frazee 257c15ec1f ios buildNumber 2 2023-08-17 18:59:55 -07:00
Paul Frazee 2198aab8b2 Fix to post meta overflow in highlighted post (#1211)
* Fix to post meta overflow in highlighted post

* Give more space to desktop web
2023-08-17 18:59:25 -07:00
Paul Frazee ee9cf7402f Bump react-native-gesture-handler@2.12.1 2023-08-17 18:30:08 -07:00
Paul Frazee 4342d0f84c 1.46 2023-08-17 18:13:32 -07:00
Paul Frazee 26f4e313ad Bump detox 2023-08-17 18:09:18 -07:00
Paul Frazee c7cad72368 Fix android postmeta alignment 2023-08-17 17:48:45 -07:00
Ansh c219fbe57a upgrade react-native-reanimated (#1198) 2023-08-17 17:47:10 -07:00
Paul Frazee 43d1f002f5 Dont suggest users that are muted or blocked (close #1185) (#1209) 2023-08-17 17:44:48 -07:00
Eric Bailey 55ad56f689 default translate to device language (#1207) 2023-08-17 15:45:36 -07:00
Paul Frazee 4a59178cd2 Update the web composer textinput to an emitter (close #1193) (#1205)
The tiptap useEditor() hook creates an awkward challenge for passing
event handlers into its plugins and native events. By introducing a
memoized editor, we should be able to shuttle events out of tiptap
without retriggering the useEditor hook. The emitter can then change
its registered handlers with each state update.
2023-08-17 13:39:59 -07:00
Paul Frazee 5e63d3164b A set of composer fixes (#1187)
* Don't insert a newline on cmd+entrl (close #1173)

* Don't linkify selected text on url-paste (close #1149)

* Disable the adult content controls if there is no media on the post (close #1169)
2023-08-16 10:46:52 -07:00
Paul Frazee 819340dd3c Shorten links in composer to reduce char usage (#1188)
* Modify toShortUrl() to always include the full domain

* Shorten links in the composer to save on characters

* Apply some limits to the link card suggester
2023-08-16 10:22:50 -07:00
Paul Frazee 5379561934 Tune link card font sizes on desktop 2023-08-16 10:10:39 -07:00
Paul Frazee 79a6819769 Tune up the link card to look nicer, show more info, and layout horizontally on web to user space more nicely (#1190) 2023-08-16 10:02:41 -07:00
Paul Frazee 884e5c9294 Fix to "Load new posts" showing sometimes when there's nothing new to show (#1191)
* Fix to feed item react key value (hopefully)

* Fix false-firing of load more (close #1028)
2023-08-16 10:00:22 -07:00
Jerry Chen b0282865fa Require bluesky-social/social-app for .github/workflows/build-and-push-* (#1152) 2023-08-15 18:50:19 -07:00
Paul Frazee 6ccfb1375a Fix tap target size of post dropdown menus (#1180) 2023-08-15 15:49:52 -07:00
Eric Bailey 0576caae36 use greater of indexedAt or machine clock (#1182)
* use greater of indexedAt or machine clock

* correct mobx usage
2023-08-15 15:18:35 -07:00
Paul Frazee 6964382bad Remove view selector offsetting (close #1174) (#1183)
* Remove view selector offsetting (close #1174)

* Remove horizontal scroll indicator in view selector
2023-08-15 15:13:40 -07:00
Paul Frazee ce1d75e164 Fix link color in dark mode (close #1170) (#1184) 2023-08-15 15:09:54 -07:00
Foysal Ahamed abbc6543f4 Repurpose report post modal and re-use for list reporting (#1070)
*  Repupose report post modal and re-use for list reporting

*  Allow reporting a feed generator

*  ♻️ Refactor report modal into one shared component for reporting different collections

*  Adjust report option selector in tests

*  Add test for list reporting

* ♻️  Refactor reason options and add options for list and feedgen

* 🧹 Cleanup remaining todo

* Fix to mutelist react keys

* Fix regression from rebase

* Improve customfeed mobile header

---------

Co-authored-by: Paul Frazee <pfrazee@gmail.com>
2023-08-15 14:32:06 -07:00
Paul Frazee a5762c2d7d Clear caches on session change (#1179) 2023-08-15 12:55:51 -07:00
Eric Bailey 331c506728 fix prefetching lightbox images (#1163)
* fix prefetching lightbox images

* use array signature

* fix other RN native image usage

* delay prefetching to allow UI to re-render
2023-08-15 12:53:03 -07:00
Ansh 77178844fd [APP-798] add key to ProfileCardPills render method (#1158)
* add key to `ProfileCardPills` render method

* More reliable key generation

---------

Co-authored-by: Paul Frazee <pfrazee@gmail.com>
2023-08-15 11:32:57 -07:00
Paul Frazee 94527a4d9e 1.45 2023-08-15 10:08:07 -07:00
Paul Frazee f4891a47a1 Fix to feeds view in profiles (#1171)
* Fix load of feeds tab

* Add e2e test for profile feeds
2023-08-15 09:59:49 -07:00
Eric Bailey a3af2a2b85 use user.handle response from getProfile, not getSession (#1165) 2023-08-15 09:59:34 -07:00
Ansh 4d953f0504 Fixes #1124: Trim alt text before saving (#1159)
* trim alt text before setting

* Drop `trim` from helpers.ts
2023-08-14 11:41:44 -05:00
Paul Frazee 003a75fd8e Build increment 2023-08-11 08:50:59 -07:00
Paul Frazee 993026cbc4 Fix: close keyboard when viewing the self-label modal (#1153) 2023-08-11 08:50:24 -07:00
Paul Frazee 821b9ba6e6 1.44 2023-08-10 16:54:52 -07:00
Paul Frazee c8bc3a1822 Test fix 2023-08-10 16:54:13 -07:00
Paul Frazee 79d78b5f59 Fix: set background color of the view selector (#1151) 2023-08-10 16:47:34 -07:00
Paul Frazee e5b7a894a3 More useful block-state information in embeds (#1150)
* More useful block-state information in embeds

* Tune copy
2023-08-10 16:25:11 -07:00
Paul Frazee ac6f6eef66 Replace the emojis in the self-labeler with a remove button (#1147) 2023-08-10 12:54:27 -07:00
Eric Bailey cc3fcb1645 Adds profile media tab (#1137)
* add media tab

* fix loading state

* cleanup

* update naming

* upgrade api package

* fix load state

* add scroll view to tabs

* fix overflow on mobile web
2023-08-10 10:50:37 -07:00
Paul Frazee 03d152675e Add self-labeling controls (#1141)
* Add self-label modal

* Use the shield-exclamation icon consistently on post moderation

* Wire up self-labeling

* Bump @atproto/api@0.6.0

* Bump @atproto/dev-env@^0.2.3

* Add e2e test for self-labeling

* Fix types
2023-08-09 17:34:16 -07:00
Eric Bailey 48813a96d6 wait for list memberships to load before becoming interactive (#1140)
* wait for list memberships to load before becoming interactive

* add spinner

* remove secondary spinner
2023-08-09 15:50:40 -07:00
Paul Frazee b5511e1450 Bump @atproto.api@0.5.4 2023-08-08 16:24:19 -07:00
Paul Frazee bbe9861eef Add alerts to embeds (#1138)
* Add alerts to embeds

* Add images to the mock data

* Fix types
2023-08-08 15:54:36 -07:00
Paul Frazee e51dbefd0a Fix to feed discovery (#1134)
* Fixes to debounce

* Less aggressive debounce
2023-08-08 11:56:34 -07:00
Paul Frazee 72e459f3e2 Fix rendering of feed and list post-embeds (close #1131) (#1135) 2023-08-08 11:34:33 -07:00
Paul Frazee 3c29a1be4d Fix to react-key uniqueness in feeds (#1136) 2023-08-08 11:34:19 -07:00
Eric Bailey 872a7f93f4 Merge pull request #1133 from bluesky-social/eric/fix-thread-ui
fix thread spacing and reply lines
2023-08-08 12:32:44 -05:00
Eric Bailey c48ff1855c remove unused styles 2023-08-08 11:36:48 -05:00
Eric Bailey 8cb076f5fd fix thread spacing and reply lines 2023-08-08 11:34:48 -05:00
Eric Bailey f7a0ea3e13 Merge pull request #1132 from bluesky-social/eric/fix-reply-line
fix long thread styling
2023-08-08 10:21:21 -05:00
Eric Bailey 85b4de701b fix long thread styling 2023-08-08 10:08:57 -05:00
Paul Frazee 7bbaccad1c Fix: dont use profile preview modal on mobile web (#1125) 2023-08-07 15:01:26 -07:00
Paul Frazee 89fc975a15 Perf: switch to stable react keys (#1113) 2023-08-05 11:29:55 -07:00
Eric Bailey d53cbb91bb fix post thread feedback regression (#1109) 2023-08-04 13:48:22 -07:00
Paul Frazee f63ed57e3b Fix repeated firing of scroll into view on large threads (#1108) 2023-08-04 13:48:07 -07:00
Paul Frazee 5d2d0976ba Increment ios build number 2023-08-04 12:14:53 -07:00
Paul Frazee d9cf37aecf Collection of small UI fixes & improvements (#1104)
* Fix black bar appearing in link card images

* Include QPs in posts cache

* Fix like color for feed likes in notifications

* Fix post embed spacing
2023-08-04 12:14:21 -07:00
Eric Bailey c42b9c1b4e fix feed UI alignment issues (#1106)
* fix feed alignment issues

* fix reason line alignment
2023-08-04 12:13:41 -07:00
Paul Frazee c4459ec4e8 Simplify the after-post logic to avoid a UI bug (#1105) 2023-08-04 09:26:09 -07:00
Paul Frazee b154d3ea21 Labeling & moderation updates [DRAFT] (#1057)
* First pass moving to the new labeling sdk (it compiles)

* Correct behaviors around interpreting label moderation

* Improve moderation state rendering

* Improve hiders and alerts

* Improve handling of mutes

* Improve profile warnings

* Add profile blurring to profile header

* Add blocks to test cases

* Render labels on profile cards, do not filter

* Filter profiles from suggestions using moderation

* Apply profile blurring to ProfileCard

* Handle blocked and deleted quote posts

* Temporarily translate content filtering settings to new labels

* Fix types

* Tune ContentHider & PostHider click targets

* Put a warning on profilecard label pills

* Fix screenhider learnmore link on mobile

* Enforce no-override on user avatar

* Dont enumerate profile blur-media labels in alerts

* Fixes to muted posts (esp quotes of muted users)

* Fixes to account/profile warnings

* Bump @atproto/api@0.5.0

* Bump @atproto/api@0.5.1

* Fix tests

* 1.43

* Remove log

* Bump @atproto/api@0.5.2
2023-08-03 22:08:30 -07:00
Jake Gold 3ae5a6b631 bskyweb: publish Bluesky IPs 2023-08-03 11:02:34 -07:00
Paul Frazee 96280d5f1a Improve the profile preview with "swipe up to view" and local cache optimization (#1096)
* Update the ProfilePreview to use a swipe-up to navigate

* Use the profile cache to optimize load performance

* Hack to align the header in the profile preview against the screen view

* Fix profiles cache logic to ensure cache is used

* Fix dark mode on profile preview
2023-08-03 10:25:17 -07:00
Eric Bailey 1211c353d0 resolve did before loading feed (#1092)
* resolve did before loading feed

* add loader

* wrap with authRequired, handle errors
2023-08-03 10:00:38 -07:00
Paul Frazee a63f97aef2 Use a post and handle-resolution cache to enable quick postthread loading (#1097)
* Use a post and handle-resolution cache to enable quick postthread loading

* Fix positioning of thread when loaded from cache and give more visual cues

* Include parent posts in cache

* Include notifications in cache
2023-08-03 09:44:43 -07:00
William 7256169506 Change button based on option (#1077) 2023-08-02 09:56:47 -07:00
Ansh f33fdb97b6 change copy in `InviteCodes.tsx (#1082) 2023-08-02 09:34:39 -07:00
Emily Liu 869cc3aaae update readme (#1083)
Co-authored-by: Emily Liu <emilyliu732l@gmail.com>
2023-08-02 09:32:13 -07:00
Paul Frazee 4effa88aec 1.42 2023-07-28 21:14:02 -05:00
Paul Frazee 1195f28992 Fix keyboard support on the dropdown (#1073)
* Fix: dropdown now supports accessibility labels and keyboard controls

* Fix event propagation around the post dropdown
2023-07-28 18:12:21 -05:00
Paul Frazee 45da8a86c9 Fix to tests 2023-07-28 16:32:55 -05:00
Ansh 3b8b562268 [APP-737] Accessible native dropdown menu (#988)
* fix comments

* add zeego package

* get basic native dropdown working

* add separator and icon components

* refined native dropdown component

* add android build properties to app.json

* move `PostDropdownBtn` to its own component

* fix selectors issue

* move `PostDropdownBtn` to its own component

* fix hitslop

* fix post dropdown hitslop

* fix android dropdown icons

* move `UserAvatar.tsx` to native dropdown

* use native dropdown in `ProfileHeader.tsx`

* use native dropdown in `PostThreadItem.tsx`

* use native dropdown in `UserBanner.tsx`

* use native dropdown in `CustomFeed.tsx`

* replace `testId` with `testID` (which is what is used everywhere)

* move `Settings.tsx` to use native dropdown

* create jest mocks for zeego

* create jest mock for `zeego/dropdown-menu`

* web styles for native dropdown

* remove example native dropdown

* adjust web styles

* fix propagation

* fix pressable in `Settings.tsx`

* animate dropdown on web

* add keyboard nav and hover styles

* add hitslop to constants

* add comments to NativeDropdown component

* temporarily removed android icons

* add testID to PostDropdownBtn

* add testID back to all NativeDropdown button implementations

* add postDropdownBtn testID

* add testID to dropdown items

* remove testID from dropdown menu item

* refactor home-screen tests for native dropdown

* refactor profile-screen tests for native dropdown

* refactor thread-muting tests for native dropdown

* refactor thread-screen tests for native dropdown

* fix dropdown color for post dropdown button

* remove icons from android dropdown menu

* fix `create-account.test.ts`

* fix `invite-codes.test.ts`
2023-07-28 16:00:37 -05:00
Foysal Ahamed eec300d772 List cleanup on remove (#1069)
* 💄 Hide Add to List option on own profile

*  Remove Lists tab when last list is removed

*  Add listener to list delete on profile screen

*  Only show save changes in list modal when changes are made
2023-07-28 11:04:27 -05:00
Ansh 38d78e16bf Search custom feeds (#1031)
* paginate custom feeds

* basic search

* update `@atproto/api`

* use search from the API

* debounce search for 200ms
2023-07-28 10:29:37 -05:00
Foysal Ahamed 8e9b8b6b36 💄 Hide Add to List option on own profile (#1068) 2023-07-27 13:07:02 -05:00
Paul Frazee 49356700c3 [APP-782] Support invalid handles correctly (#1049)
* Update profile link construction to support handle.invalid

* Update list links  to support using handles

* Use did for isMe check to ensure invalid handles dont distort the check

* Shift the red (error) colors away from the pink spectrum

* Add ThemedText helper component

* Add sanitizedHandle() helper to render invalid handles well

* Fix regression: only show avatar in PostMeta when needed

* Restore the color of likes

* Remove users with invalid handles from default autosuggests
2023-07-27 10:50:12 -05:00
Steven 5a0899b989 fix: invisible url when only a protocol (#1059)
* fix: invisible url when only a protocol

- Fixes https://github.com/bluesky-social/social-app/issues/1058

* fix: handle more cases when url is not http/https
2023-07-27 10:49:37 -05:00
David Buchanan 44b3929240 Update icons.tsx - Simplify SatelliteDishIcon paths (#1048)
This prevents rendering jank
2023-07-25 10:21:17 -05:00
GitStart c1580dae1f fix: ALT text entered in the compose dialog is erased when you press "Edit" again (#1054) 2023-07-25 09:47:26 -05:00
Logan Rosen 806be6f722 Bump react-native-paste-input; remove hack (#1051) 2023-07-25 09:45:38 -05:00
Ansh bb99a234e1 [APP-720] Paginate custom feeds (#1030)
* paginate custom feeds

* Fix loading state bug

* DRY code up
2023-07-21 17:39:06 -05:00
Eric Bailey bf00d49863 #995 use asAnchor on external embeds (#1043)
* use asAnchor on external embeds

* add noFeedback back, fix TouchableWithoutFeedback anchor handling
2023-07-21 17:17:35 -05:00
Ansh 7e1dac0439 fix mute list overflow bug (#1041) 2023-07-20 11:48:23 -07:00
Ansh 30ac9259c7 [APP-775] Add Welcome screen after account creation (#1038)
* add comments to step 1-3

* add onboarding screen

* add analytics for onboarding tracking

* fix useEffect

* change text

* change icon size

* put onboarding into bottom sheet modal instead of react navigation

* wip

* Simplify the type validation

* Fix: only trigger onboarding modal when account creation succeeds

* Add the 'session-ready' event which fires when the new session is stable

* Use the 'session-ready' event to trigger the onboarding modal

* update copy

* update copy

---------

Co-authored-by: Paul Frazee <pfrazee@gmail.com>
2023-07-19 23:50:42 -07:00
Ansh 3517d9fa28 Hotfix mobile web styling issues (#1039)
* remove hardcoded height for bottom bar

* prevent overflow of tab bar on mobile web
2023-07-19 15:00:40 -07:00
Eric Bailey 302490cb31 wrap interval in runInAction (#1037) 2023-07-19 14:24:35 -05:00
Eric Bailey 0ae52e91ce #984 Updating indexedAt timestamps (#1024)
* add TimeElapsed util component, integrate into PostThreadItem

* integrate into posts

* use consistent naming

* use mobx and single interval for TimeElapsed
2023-07-19 12:16:57 -05:00
Eric Bailey 4515559b1a #929 Wrap PasteInput updates in a setTimeout (#1033)
* wrap PasteInput updates in a setTimeout

* just wrap the whole callback
2023-07-19 12:08:40 -05:00
Sam Brownstone abd2c8e695 Fix cursor not being a pointer on profile back button (#1022)
* Fix cursor not being a pointer on profile back button

* Change TouchableOpacity to Pressable

* switch back to TouchableWithoutFeedback

---------

Co-authored-by: Ansh Nanda <anshnanda10@gmail.com>
2023-07-18 15:40:36 -07:00
Eric Bailey a6bb38ee51 Merge pull request #1032 from bluesky-social/fix-1001-modal-scrolling
#1001 Modal content overflow
2023-07-18 16:46:22 -05:00
Eric Bailey 8a66af11ae Merge pull request #1011 from bluesky-social/eric/app-741-add-pre-commit-hook
add pre commit hook for linting/formatting
2023-07-18 16:45:24 -05:00
Eric Bailey 9ef54814f0 fix lint 2023-07-18 15:26:04 -05:00
Eric Bailey 1ca5792165 create explicit relationship between values 2023-07-18 15:18:56 -05:00
Eric Bailey 17deaaa7e7 use calc 2023-07-18 15:15:02 -05:00
Ansh Nanda b59ca5f343 undo commit 2023-07-18 13:05:17 -07:00
Ansh Nanda e0f30c716a test 2023-07-18 13:04:29 -07:00
Eric Bailey ea567362a1 Merge pull request #1025 from GitStartHQ/BLUESKY-OSS-8
[Web] A lot of post content in a modal causes parts of modal to become inaccessible
2023-07-18 14:57:21 -05:00
Paul Frazee 8d47e6f112 Merge branch 'therealjeffg-term_violation_addition' into main 2023-07-18 11:47:22 -05:00
Paul Frazee 9ac820baac Tweak copy 2023-07-18 11:47:10 -05:00
Paul Frazee ff61fc3c1d Merge branch 'term_violation_addition' of https://github.com/therealjeffg/social-app into therealjeffg-term_violation_addition 2023-07-18 11:43:14 -05:00
gitstart dea8b74580 fix hidden parts on modal 2023-07-18 07:05:57 +00:00
Eric Bailey aa4b79e77a handle failed custom feeds on home screen (#1012)
* handle failed custom feeds on home screen

* remove unnecessary edit

* make sure edits to pinned feeds are handled and reloaded

* fix lint
2023-07-17 17:31:56 -05:00
Paul Frazee bfed96bd7b Bump ios build number 2023-07-17 16:17:49 -05:00
Paul Frazee ad15ee31f3 [APP-773] Change waitlist explainer copy (#1021)
* Change waitlist explainer copy

* Restore the waitlist modal snap point to 80% to account for smaller phones

* Tune copy further
2023-07-17 16:17:14 -05:00
Jeff Griffiths 76aa792afd adds additional reason for reporting an account profile 2023-07-14 09:50:55 -07:00
Eric Bailey 2f63afc92e fix typo 2023-07-13 17:39:43 -07:00
Eric Bailey c75383bf9c move eslint dep to devDeps 2023-07-13 15:56:29 -07:00
Eric Bailey 5a2401ada9 conditionally install husky 2023-07-13 15:55:30 -07:00
Eric Bailey c83573bf97 set up lint-staged via husky
(cherry picked from commit 98151fafbd9b248fce62f013cecd3d20d4d17006)
2023-07-13 15:35:56 -07:00
Paul Frazee 3bd891b01a 1.41 2023-07-07 12:01:31 -05:00
Paul Frazee 237e957d16 Fixes and improvements to the Profile Preview modal (#992)
* Fix: use more reliable navigation method

* Fix: show lightbox over the active modal

* Fix: close the profile preview on navigation

* Factor out UserPreviewLink and add preview behavior to notifications

* Fix postmeta overflow on web

* Fix lint
2023-07-07 12:00:17 -05:00
Paul Frazee d8aded7b15 1.40 2023-07-06 21:24:01 -05:00
Paul Frazee 6f69157269 Post UI updates (Profile Preview on mobile) (#990)
* Update postmeta to put the timestamp on the right side on mobile

* Drop the two-line PostMeta mode

* Add ProfilePreview modal

* Tune PostMeta to give the best behavior possible for a given platform

* Remove old showFollowBtn attributes

* Fix style issue

* Switch the follow button in the profile header to use the inverted color for consistency with the rest of the app

* Fix lint

* Fix darkmode

* Tune the profile preview footer

* Better analytics choice
2023-07-06 21:12:54 -05:00
Ansh df7552135a [APP-703] Android horizontal scroll registers as tap (#960)
* use Touchables from react-native-gesture-handler

* upgrade `react-native-gesture-handler` to latest version

* add FixedTouchableHighlight for android

* add workaround comment

* wait for animations to complete before loading data

* downgrade RNGH back to the version we had
2023-07-06 20:41:27 -05:00
Paul Frazee bf1785765d [APP-729] Replace the ImageHider blurring effect with a simpler and more reliable card (#959)
* Replace the ImageHider blurring effect with a simpler and more reliable card

* A few improvements to ImageHider layout

* Simplify the ImageHider a bit more

* Small web layout tweak
2023-07-06 20:29:52 -05:00
Ansh b06304a253 [APP-28] add status page link (#987)
* add status page to mobile

* add status page URL to super slow loading screen

* store STATUS_PAGE_URL in constants.ts
2023-07-06 20:29:18 -05:00
Paul Frazee 831b367eb9 [APP-736] Clarify hate-group moderation as it's currently used (#985)
* Clarify hate-group moderation as it's currently used

* Update src/lib/labeling/const.ts

Co-authored-by: Emily Liu <emilyliu7321@gmail.com>

---------

Co-authored-by: Ansh <anshnanda10@gmail.com>
Co-authored-by: Emily Liu <emilyliu7321@gmail.com>
2023-07-06 20:28:31 -05:00
Paul Frazee e14c9783e0 [APP-735] Post language improvements (#982)
* Fix composer character-counter bouncing around UI elements

* Fix composer toolbar padding when keyboard is dismissed on iOS

* Use the full name of the language in the composer footer

* Add headings to the DropdownButton

* Update the composer language control to use a simpler dropdown

* Fix lint

* Add translate link to Post component used in notifications

* Fix lint
2023-07-06 20:28:10 -05:00
Jonty Wareing f05c2f06d6 Fix missing post text in noscript post view (#981) 2023-07-06 12:12:00 -05:00
Paul Frazee edb37e2c06 1.39 2023-07-05 21:22:04 -05:00
Paul Frazee d442b1cd4c Update support links (#977)
* Add the new zendesk feedback form URL

* Dont wrap handle or display name in the drawer

* Add help link

* Tune drawer footer layout

* Type fixes

* make helpdesk urls more composable for future

* fix typo

---------

Co-authored-by: Ansh Nanda <anshnanda10@gmail.com>
2023-07-05 19:58:29 -05:00
Paul Frazee 3a6073abb8 Added instructions for .well-known method (supersedes #887) (#979)
* Added instructions for .well-known method

* Factor out SelectableBtn

* Rework the ChangeHandle modal to be a little clearer

* Fix lint

* Fix desktop layout

---------

Co-authored-by: Haider Ali Punjabi <haiderali@cyberservices.com>
Co-authored-by: Haider Ali Punjabi <haideralipunjabi@hackesta.org>
2023-07-05 19:56:42 -05:00
Ansh fe32730025 [APP-702] Render Custom Feed likes properly (#980)
* render href for custom feed likes properly

* fix custom feed text in push notification

* fix custom feed accessibility title

* fix lint
2023-07-05 19:56:26 -05:00
Ændra Rininsland 99aa38e3ca Fixes vertical alignment in feed tab bar (#974)
The tab bar items need `justify-content: center` on them or else they can appear unaligned when a feed name has an emoji character in it.
2023-07-05 18:26:18 -05:00
Ikko Eltociear Ashimine 3498c1d239 fix: typo in feed-manip.ts (#967)
prefered -> preferred
2023-07-05 09:49:45 -07:00
Paul Frazee 9750c393a0 1.38 2023-07-03 19:05:54 -05:00
Paul Frazee c8eeb6ba1c Fix: re-add the scroll boundary to avoid minimal shell at top of screen (#956) 2023-07-03 18:36:49 -05:00
Ansh 40a872612f don't throw, but log OTA listener errors (#958) 2023-07-03 18:17:28 -05:00
Ansh 269c0687fd track successful account creation (#955) 2023-07-03 18:17:03 -05:00
Paul Frazee 343154a403 Fix reply-root reference (fixes thread muting) (#957) 2023-07-03 17:54:09 -05:00
Paul Frazee 696bffe832 Add alt text validation option to user preferences (supersedes #913) (#914)
* Add alt text validation option to user preferences

* Fix typos/linting issues

* Update accessibility setting to match styles

* Update the required alt text reminder to go away once it's added

---------

Co-authored-by: Emma Fuller <emma@emmafuller.dev>
2023-07-03 15:58:07 -05:00
Paul Frazee bc55241c9a [APP-724] Collection of accessibility fixes (#949)
* Fix: include alt text on the web lightbox image

* a11y: Dont read the 'ALT' label

* a11y: remove a wrapper behavior from posts

This appears to have been introduced with the goal of creating meta
actions on posts, but the behavior seems counter-productive. The
accessibility inspector was unable to access individual items within
the post and therefore most content was simply skipped.

There may be a way to support the post actions without losing the
ability to access the inner elements but I couldnt find it. -prf

* a11y: apply alt tags to image wrappers so they get read

* a11y: set Link accessibilityLabel to the title if none set

* a11y: skip the SANDBOX watermark

* a11y: improve post meta to not read UI and give a useful date

* ally: improve post controls

* a11y: add labels to lightbox images on mobile

* fix types
2023-07-03 15:57:53 -05:00
Paul Frazee 0163ba0af8 Fix: let images on desktop web take more vertical space (#953) 2023-07-03 15:57:39 -05:00
Ansh edff1992b1 Add permission checks before saving image (#945)
* catch permission errors when saving image to album

* Save photos to media library alone (not an album)

---------

Co-authored-by: Paul Frazee <pfrazee@gmail.com>
2023-07-03 13:17:12 -05:00
Paul Frazee 7cf0db7d14 Fix saved feeds screen layout on web (close #920) (#943) 2023-07-02 15:59:07 -05:00
Markus Amalthea Magnuson 1ea2a94b9b Fix keyboard navigation on settings page. (#923) 2023-07-02 15:18:42 -05:00
Markus Amalthea Magnuson 6768e3ff71 Make sidebar avatar keyboard-clickable. (#921) 2023-07-02 15:07:39 -05:00
Paul Frazee 97f0d3431c fix: stabilize minimalShellMode toggling by introducing modified thresholds (supersedes #938) (#942)
* add separate scroll up/down thresholds to prevent rapid state changes

* set `DY_LIMIT_DOWN` to 150

* Use a different scroll dy limit on web/mobile

---------

Co-authored-by: Sehyun Chung <sehyun.chung@icloud.com>
2023-07-02 15:03:16 -05:00
Ansh 7ee7d968e5 [APP-708] Onboarding revamp (#939)
* Move Discover New Feeds button to the top of the MultiFeed

* add discover custom feeds to empty following page
2023-07-02 13:30:02 -05:00
Dylan Smith 2be2498940 Use consistent font weight for repost count (#940) 2023-07-02 13:25:47 -05:00
Paul Frazee 7597fab290 1.37 2023-07-01 14:30:36 -05:00
Paul Frazee 2e93e4a919 Performance improvement (#937)
* Perf: Wait until a feed is visible in the home screen before fetching data

* perf: update feed checkForLatest to use limit=1

* Fix lint
2023-07-01 14:25:13 -05:00
Paul Frazee 93689ad73d Fix: correctly handle the case where the user has no languages configured 2023-06-30 12:21:30 -05:00
Paul Frazee 948b324cd8 1.36 2023-06-30 12:07:46 -05:00
Paul Frazee d50ba22820 Fix: reset navigation state on account switch (#933) 2023-06-30 11:38:37 -05:00
Paul Frazee 010588ee7c Fix thread muting (#932) 2023-06-30 11:38:22 -05:00
Paul Frazee ed5a88d9d8 [APP-718] Improvements and fixes to language handling (#931)
* Add locale helpers for narrowing languages

* Add a translate link to posts in a different language

* Update language filtering to use narrowing when multiple declared

* Fix a few more RTL layout cases

* Fix types
2023-06-30 11:35:29 -05:00
Paul Frazee 48844aa4c7 Include a notice during account creation for under-18 users (#927) 2023-06-30 11:34:52 -05:00
Paul Frazee c72e24f841 [APP-716] Add 'save image' button to the lightbox (#926)
* Add 'save image' button to the lightbox

* Fix types

* Fix types
2023-06-30 11:34:04 -05:00
Paul Frazee 5fcca17129 1.35 2023-06-28 13:41:42 -05:00
Paul Frazee eb05c4cd6f [APP-692] Accessibility font-scaling fixes (#922)
* Fix: enable font scaling in the composer text input

* Fix: cancel button in search no longer wraps

* Fix: report modals no longer clip off the bottom with scaled fonts

* Fix scrolling on android

* Allow wrapping in dropdown items until we can handle proper sizing
2023-06-28 13:38:21 -05:00
Paul Frazee df3589f207 Fix: hide the keyboard before showing the discard modal (#925) 2023-06-28 13:37:19 -05:00
Paul Frazee 8d32f3de37 Merge branch 'main' of github.com:bluesky-social/social-app into main 2023-06-28 09:59:16 -05:00
Paul Frazee c98bdcf86d 1.34 2023-06-27 20:56:45 -05:00
Paul Frazee f8d218e11a Simulator fixes (#918)
* Update the mock server to use the dev-env to manage the server

* Fix list testIDs

* Fix the invite test construction

* Remove leftover test hardcode
2023-06-27 20:55:46 -05:00
Paul Frazee dce80be075 Fix copy and dark mode styles in the home feed preferences (#915) 2023-06-27 15:00:00 -05:00
Paul Frazee 0921dd4335 Fix some broken links on policy docs (#916) 2023-06-27 14:59:42 -05:00
Paul Frazee a733b5b9f4 Revert change to the cancel button in the composer (#917) 2023-06-27 11:56:26 -07:00
Paul Frazee 1024f37a54 Merge branch 'benharri-forks-skip-confirmation-for-empty-post' into main 2023-06-27 11:04:33 -05:00
Paul Frazee 0f9429605d Merge branch 'skip-confirmation-for-empty-post' of https://github.com/benharri-forks/social-app into benharri-forks-skip-confirmation-for-empty-post 2023-06-27 11:01:25 -05:00
Ansh a8bbaa06c7 [APP-705] Metrics revamp pt2 (#896)
* export track function from analytics.tsx

* fix create account tracking

* fix tracking sign in

* add custom feed events

* fix type errors

* refactor create post event

* add profile follow & unfollow events

* refactor PostsFeedSliceModel into its own file

* refactor PostThreadItemModel into its own file

* reorganize code a lil bit

* refactor post-thread-item to use post-feed-item model under the hood

* add post events

* add post reply tracking

* track custom feed load more

* track list subscribe and unsubscribe
2023-06-27 10:11:05 -05:00
Paul Frazee bfaa6d73f3 Improvements to the alt text behaviors in the composer (#910)
* Add an image preview in the alt modal

* Composer: add info about alt text and a green checkmark when done

* Shrink the alt visual indicator a bit so it doesnt obscure the image

* Fix typo

* Fix: avoid requiring multiple tabs to save alt text

* update react-native-screens

* Improve the alt text help tip

* Remove redundant hints

---------

Co-authored-by: Ansh Nanda <anshnanda10@gmail.com>
2023-06-27 09:52:49 -05:00
Ansh 25b3e14926 make discard btn more apparent in UI (#912) 2023-06-26 17:10:04 -07:00
Paul Frazee cd21a7dfc9 Fix Voice Control input on iOS (closes #891) (#911)
* Add patch to fix issue with VoiceControl on iOS

* Fix .patch
2023-06-26 18:53:35 -05:00
Ben Harris 2e082b6977 fix cancel post behavior
- prompt now appears for the cancel button
- no prompt when post is empty
- appease eslint
2023-06-26 16:28:28 -04:00
Ansh b9abd444e5 [APP-657] Add share list functionality (#863)
* replace delete list button text with icon

* fix mute list styling on desktop

* add share button to nav bar on a list

* fix styling when on profile

* bug: add key to ImageHorzList

* clean up code & refactor

* fix styling for ListItems

* create a reusable ListActions component for actions on a list

* remove dead styles

* add keys to ListActions

* add helpers to set list embed

* render list embeds

* fix list sharing on web

* make style prop optional in ListCard

* update `@atproto/api` to `0.3.13`
2023-06-26 12:15:39 -05:00
Markus Amalthea Magnuson 1666a747eb Add a11y details to profile page compose button. (#909)
This adds accessibility role, label and hint to the compose button on profile pages, to match the same button in all other views.
2023-06-26 12:14:51 -05:00
Ansh 08804f265e [APP-690] better handling of post languages language filtering (#893)
* add SelectLangBtn

* memoized objects that are created to reduce re-creation on re-render

* add langs when uploading post

* only send the top 3 languages otherwise backend will throw error

* mv ContentLanguagesSettings to folder

* add post languages settings modal and state

* fix typos

* modify feed manip to also check langs label on post

* Fix tests

* Remove log

* Update feed-manip.ts

* Fix syntax errors

* UI tuneups

* Show the currently selected languages in the composer

* fix linting

* Use a bcp-47 matching function

* Fix a duplicate language issue

* Fix web

* Dont include lang in prompt

* Make select language btn an observer

* Keep device languages on top of language selection UIs

* Fix android build settings

* Enforce a max of 3 languages in posts

* Fix tests

* Fix types

---------

Co-authored-by: Paul Frazee <pfrazee@gmail.com>
2023-06-23 12:48:52 -05:00
Logan Rosen 9b19a95e63 Bump @types/react to ^18 and adjust types (#889) 2023-06-22 12:40:32 -04:00
Paul Frazee 180697b66b 1.33 2023-06-16 16:28:46 -05:00
Paul Frazee 46c65958bc Merge branch 'estrattonbailey-eric/reply-settings-toggle' into main 2023-06-16 16:02:56 -05:00
Paul Frazee 20cdede5e1 Small style fixes 2023-06-16 16:02:26 -05:00
Paul Frazee 31c9e02c0a Update feed settings icon 2023-06-16 15:56:27 -05:00
Eric Bailey 20f77ae7b7 feat: styling/copy cleanup 2023-06-16 15:17:09 -05:00
Eric Bailey 73c6afc7fb fix: comment 2023-06-16 15:16:30 -05:00
Eric Bailey 896aea9837 feat: add home feed preferences settings modal and tuners 2023-06-16 15:01:43 -05:00
Ansh 17e7590bcd [APP-511] metrics overhaul: frontend work (#506)
* WIP

* fix types and update imports

* wip

* tagged events that should be server side

* remove server-side analytics

* remove useless import

* add additional profile header events

* remove useless import

* track follow/unfollow clicks

* add missing types
2023-06-15 16:45:14 -05:00
Paul Frazee 1695ae34db 1.32 2023-06-14 23:17:25 -05:00
Paul Frazee 56ec3bf89e Move policy docs to blueskyweb.xyz (#881) 2023-06-14 20:00:28 -05:00
Paul Frazee 3663ee57f3 Add testnet warning (#880)
* Add testnet warning

* Add watermarks to posts

* Call the test environment the Sandbox
2023-06-14 20:00:16 -05:00
Ansh 775b5e6578 add checks to make sure we are on web (#879) 2023-06-14 17:20:46 -05:00
Tymoteusz Boba 272ddeb7e2 Upgrade Reanimated from 2.14.4 to 3.1.0 (#753)
* Update Reanimated API to v3

* Fix failing tests after updating Reanimated

* Include changes from package.json in Reanimated library to patch

* upgrade reanimated to 3.3.0

* remove patch

* upgrade bottom sheet

---------

Co-authored-by: Ansh Nanda <anshnanda10@gmail.com>
2023-06-14 16:35:06 -05:00
Ansh 9b9f339e3e [APP-686] Fix mobile web styling (#876)
* fix desktop window size

* fix desktop window size

* add listener for resize events

* fix when going from mobile to web

* only add eventListener for web
2023-06-14 15:26:44 -05:00
Ansh 71af9fd04b add error handling to updateCache for saved-feeds (#878) 2023-06-14 15:14:56 -05:00
Markus Amalthea Magnuson c060cd4158 Various case and text fixes. (#757)
* Various case and text fixes.

* Update some labels per requested changes.

---------

Co-authored-by: Ansh <anshnanda10@gmail.com>
2023-06-13 11:35:13 -07:00
Ivan Silvestre 37782ae60b Desktop: prevent unnecessary X scroll between 1230px and 1260px (#828)
* Desktop: prevent unnecessary X scroll between 1230px and 1260px

* imports structure without changes

* re add dot to text
2023-06-13 11:29:36 -07:00
GitStart c20f8937cb UX: account deletion dialogue does not tell me which account I'm deleting (#835)
* fix: account deletion dialogue does not tell me which account I'm deleting

* update styles and display name
2023-06-13 11:24:57 -07:00
Paul Frazee 1a12fa5775 Add temporary appview-proxy header toggle (#874) 2023-06-13 13:10:42 -05:00
Paul Frazee 6efbe820d9 Merge branch 'loganrosen-loganrosen/heading-elements' into main 2023-06-12 17:26:17 -05:00
Paul Frazee bc2bf20e08 Update info screen titles to use H1 2023-06-12 17:25:52 -05:00
Paul Frazee 9c8e0ab33a Merge branch 'loganrosen/heading-elements' of https://github.com/loganrosen/social-app into loganrosen-loganrosen/heading-elements 2023-06-12 17:06:39 -05:00
Paul Frazee abdb4aace6 Merge branch 'main' of github.com:bluesky-social/social-app into main 2023-06-12 17:06:17 -05:00
Markus Amalthea Magnuson 571285c2b4 Fix image-only posts not being visible in custom feeds. (#871) 2023-06-12 17:05:31 -05:00
Logan Rosen 9232daba7a Move useWebMediaQueries() above return (#872) 2023-06-11 20:12:50 -07:00
Markus Amalthea Magnuson 8f74c7b949 Fix image-only posts not being visible in custom feeds. 2023-06-12 01:40:59 +02:00
Logan Rosen 55cf53cfd5 Use native heading elements 2023-06-10 19:12:26 -04:00
Paul Frazee 8d03ab9399 1.31 2023-06-09 17:32:40 -05:00
Logan Rosen 9ac8c44a6a Use Linking.openSettings() so it works on Android (#865) 2023-06-08 20:47:24 -07:00
Logan Rosen cae615769c Link to instructions for creating Sentry auth token (#864) 2023-06-08 19:54:37 -07:00
Ansh 6a967cce00 make build version text copy on press (#862) 2023-06-08 14:40:20 -05:00
Ansh b3e7801796 Revert "show date after 7 days closes #754" (#860) 2023-06-08 11:32:56 -05:00
Ansh 007d12e182 push instead of navigate (#861) 2023-06-07 17:55:25 -05:00
Ansh 0be14a1b46 [APP-680] Allow users to add details when reporting (#854)
* allow user to add text when reporting post

* add DMCA override

* increase modal size

* fix dark mode text color

* re-organize components

* add details option when reporting account

* hard-code modal size so it works on smaller devices

* fix modal on web

* Remove outline from textarea focus

* Tweak some styles

* Fix lint

---------

Co-authored-by: Paul Frazee <pfrazee@gmail.com>
2023-06-07 11:11:04 -05:00
Ansh fc12a1205c allow image to clicked to go to post in notificaitons (#858) 2023-06-07 09:57:49 -05:00
Logan Rosen bdcdb4e4dc Use expo-image-picker on Web (#847) 2023-06-06 09:38:58 -05:00
Keith Cirkel a67eaa6ace remove X-UA-Compatible meta tag (#857)
This meta tag allows Internet Explorer 7 through 11 to change which document mode they would use. As of 2013, however, [Microsoft Edge would ignore this value except under strict circumstances](https://learn.microsoft.com/en-gb/archive/blogs/ie/living-on-the-edge-our-next-step-in-helping-the-web-just-work). This meta tag is ignored in modern versions of Edge (Chromium based).

[Internet Explorer 11 reached EOL in June 2022](https://blogs.windows.com/windowsexperience/2022/06/15/internet-explorer-11-has-retired-and-is-officially-out-of-support-what-you-need-to-know/) and today makes up an exceedingly small proportion of internet traffic.

It's unlikely that anyone is browsing on Internet Explorer, and less
likely that they're browsing on IE<11, which makes this tag redundant.

Co-authored-by: Paul Frazee <pfrazee@gmail.com>
2023-06-06 09:33:23 -05:00
Keith Cirkel b33626c3c7 remove trailing / on meta tags (#856)
* remove trailing / on meta tags

Meta tags are [void elements](https://html.spec.whatwg.org/multipage/syntax.html#void-elements) which means they are self closing, and do not require a closing tag, nor a trailing `/`.

The trailing `/` is something that was required in XHTML but [is ignored
in HTML](https://html.spec.whatwg.org/multipage/syntax.html#start-tags). Quoth the spec:

> On void elements, [the trailing slash] does not mark the start tag as self-closing but instead is unnecessary and has no effect of any kind. For such void elements, it should be used only with caution — especially since, if directly preceded by an unquoted attribute value, it becomes part of the attribute value rather than being discarded by the parser.

* remove trailing / on link tags
2023-06-06 09:32:30 -05:00
Ansh bf36101bf2 [APP-655] Password autocomplete when logging in (#838)
* remove unused styles

* refine login form to autofill password from password manager
2023-06-06 09:31:50 -05:00
Logan Rosen 8cc89a5a16 Fix dark mode detection on mobile (#852) 2023-06-05 09:40:13 -07:00
Paul Frazee 48637246c7 1.30 2023-06-02 15:38:49 -05:00
Paul Frazee 75b729a670 Merge branch 'main' of github.com:bluesky-social/social-app into main 2023-06-02 15:29:36 -05:00
Paul Frazee a2721ceb95 Fix to simulator tests 2023-06-02 15:29:27 -05:00
Ansh ba4bb46c3f [APP-107] OTA updates (#587)
* add 1000ms fallbackToCacheTimeout

* add listener via useOTAUpdate hook and show modal if update is available

* finish expo-updates setup

* setup useOTAUpdate hook

* add 1000ms fallbackToCacheTimeout

* add listener via useOTAUpdate hook and show modal if update is available

* finish expo-updates setup

* setup useOTAUpdate hook

* add OTA updates

* Update build.md

* temporarily disable ota updates

* refactor useOTAUpdate code
2023-06-02 13:27:59 -07:00
Paul Frazee ad4eaf5ed2 Move suggested follow recommendations to the server (#836)
* Move suggested follow recommendations to the server

* fix search results color in dark mode

---------

Co-authored-by: Ansh Nanda <anshnanda10@gmail.com>
2023-06-02 15:01:16 -05:00
Paul Frazee e8843ded5b Fix a bunch of type errors and add a type-check to the github workflows (#837)
* Add yarn type-check

* Rename to yarn typecheck

* Fix a collection of type errors

* Add typecheck to automated tests

* add `dist` to exluded folders tsconfig

---------

Co-authored-by: Ansh Nanda <anshnanda10@gmail.com>
2023-06-02 15:01:04 -05:00
Logan Rosen 46c9de7c18 Don't show Remove button for nonexistent avatar/banner (#833) 2023-06-02 09:51:11 -05:00
Paul Frazee 3217c7ff32 More custom-feed behavior fixes [APP-678] (#831)
* Remove extraneous custom-feed health check

* Fixes to custom feed preference sync

* Fix lint

* Remove dead code (client-side suggested posts constructor)

* Enforce the feed-fetch limit in the client if the generator fails to observe the parameter

* Bump the number of items fetched in the multifeed per feed from 5 to 10

* Reset the currently active feed when the pinned feeds change

* Some fixes to icons

* Add a prompt to load latest to the multifeed

* Remove debug
2023-06-02 09:48:53 -05:00
Paul Frazee e9c84a192b Fixes to feed preference and state sync [APP-678] (#829)
* Remove extraneous custom-feed health check

* Fixes to custom feed preference sync

* Fix lint

* Fix to how preferences are synced to enable membership modifications
2023-06-01 14:46:13 -05:00
Jake Gold f416798c5f bskyweb: add security.txt 2023-06-01 11:47:59 -07:00
Kadi Kraman d4e7355cca fix: support scroll to top on profile screen (#725)
* Support scroll to top on profile screen

* Refactor types

* Remove async

* Improve types
2023-06-01 11:00:00 -05:00
Michi Hoffmann 792d7e1a55 Fix line breaks on side bar links (#815) 2023-06-01 10:51:42 -05:00
Samuel Newman 88212eb065 translate to primary language rather than just english (#814) 2023-06-01 10:50:18 -05:00
Ansh 1fc694d488 [APP-656] make alt text expandable (#821)
* make alt text expandable

* clean code
2023-06-01 10:47:17 -05:00
Ansh 5c25d670d0 move early return to after all hooks (#823) 2023-06-01 10:41:27 -05:00
Ansh 54e5e30945 [APP-674] Fix search results tab bar (#822)
* fix search results tab bar

* Fix spacing on web

---------

Co-authored-by: Paul Frazee <pfrazee@gmail.com>
2023-06-01 10:40:26 -05:00
Jake Gold 49840f3a27 bskyweb: gzip HTTP responses + some other minor improvements (#826)
* bskyweb: gzip HTTP responses + JSON logging + minor refactoring

* reduce timeout and max header size

* add a security.txt
2023-06-01 10:22:02 -05:00
Ansh 8fde55b59b simplify how x position is calculated for scrollTo in TabBar (#820) 2023-06-01 10:19:15 -05:00
bnewbold 31a41d9b09 bskyweb: run-dev-bskyweb in debug mode (#827) 2023-05-31 20:44:50 -07:00
Ansh 189fdb1789 fix linting & type errors (#819) 2023-05-31 17:19:08 -07:00
LW 7d7410942f fix: make DesktopLeftNav not overflow screen (#762)
* fix: make DesktopLeftNav not overflow screen

* scroll: auto
2023-05-30 21:18:00 -05:00
Ansh 3cb8169a4f Fix deeplink when app is suspended (#812)
* Update Navigation.tsx

Missing If statement within the Deep Linking function that broke the ability to change state to hometab

* Updated the function correctly, I missed a bracket

* format

* add pre-existing home state when unspecified path

* Remove unnecessary log

---------

Co-authored-by: Gai <68623807+that-gai-gai@users.noreply.github.com>
Co-authored-by: that-gai-gai <kgai@protonmail.com>
2023-05-30 21:16:29 -05:00
Paul Frazee 421f29ad98 Merge branch 'ericvolp12-inherit_system_theme' into main 2023-05-30 21:13:34 -05:00
Paul Frazee 2413549fa5 Tune sizing of appearance control 2023-05-30 21:13:18 -05:00
Jaz 09ade363fd Merge branch 'main' into inherit_system_theme 2023-05-30 18:25:29 -07:00
38elements e224569a11 Remove lodash.clonedeep in package.json (#652) 2023-05-30 20:19:09 -05:00
Matin Zadeh Dolatabad fa9351534b perf: enable lazy loading for tab navigation (#711) 2023-05-30 20:16:30 -05:00
Markus Amalthea Magnuson 7d47b342d6 Make terms and notices dates less ambiguous. (#713) 2023-05-30 20:09:35 -05:00
Ollie H c0ca27b7ce Use proxy for fetching link meta (#716)
* Use proxy for fetching link meta

* Remove link meta test due to hitting proxy

* setup different staging and prod proxy URLs

---------

Co-authored-by: Ansh Nanda <anshnanda10@gmail.com>
Co-authored-by: Paul Frazee <pfrazee@gmail.com>
2023-05-30 20:08:49 -05:00
Bartosz Kaszubowski 2018558585 feat(Login): allow submitting on password field (#720) 2023-05-30 19:56:13 -05:00
Ben Harris 037713321d show package.json version on web (#717) 2023-05-30 19:53:23 -05:00
Ollie H a9a661ab58 Add ability to reply, repost (without quote post), and like posts using VoiceOver (#765)
Co-authored-by: Paul Frazee <pfrazee@gmail.com>
2023-05-30 19:50:56 -05:00
Ben Harris 7458b6f600 Move MAX_GRAPHEME_LENGTH to constants.ts (#735) 2023-05-30 19:41:56 -05:00
skipness 0239225796 fix: content trimmed in server input modal (#733) 2023-05-30 19:41:21 -05:00
Okechukwu Somtochukwu 8258317c38 [typo] correct typo was 'fo' instead of 'to' (#721) 2023-05-30 17:26:40 -07:00
Ollie H 072682dd9f Rework scaled dimensions and compression (#737)
* Rework scaled dimensions and compression

* Unbreak image / banner uploads

---------

Co-authored-by: Paul Frazee <pfrazee@gmail.com>
2023-05-30 19:23:55 -05:00
Ollie H deebe18aaa Use non-pipe translation link (#740) 2023-05-30 18:54:47 -05:00
Ansh a911250ea8 add instructions to removing sentry in build.md (#749)
Co-authored-by: Paul Frazee <pfrazee@gmail.com>
2023-05-30 18:50:52 -05:00
David Buchanan 1ca40cf1fd Update HomeIconSolid: widen the front door (#756) 2023-05-30 18:49:03 -05:00
Ben Harris ca34364cf0 Support arrow key navigation for Lightbox.web.tsx (#761)
* Support arrow key navigation for Lightbox.web.tsx

renames onEscape to onKeyDown

* appease eslint

* appease eslint again

* wrap onPressLeft and onPressRight in useCallback
2023-05-30 18:45:49 -05:00
MohammadReza Mahmoudi 442d453600 Fix profile load issue + enhancement of profile isCurrent style (#764)
* fix profile isActive issue

* fix myProfile load issue when visiting other profiles first

* fix lint issues

* change currentRouteName to currentRouteInfo
2023-05-30 18:42:22 -05:00
Paul Frazee 5d0a3e6c65 Merge branch 'main' of github.com:bluesky-social/social-app into main 2023-05-30 18:35:18 -05:00
Seth Arnold 1e5c4609a8 Update ProfileHeader.tsx (#810)
Change error message from "failed to blocK" to "failed to unblock"
2023-05-30 18:32:46 -05:00
Paul Frazee a11a1b7657 Merge branch 'show_timestamp_after_7d' of https://github.com/benharri-forks/social-app into main 2023-05-30 18:20:26 -05:00
Paul Frazee 762ab3d385 Update yarn.lock 2023-05-30 18:20:11 -05:00
Ben Harris 439135944c make eslint happy 2023-05-30 18:30:12 -04:00
Ben Harris f2cf1d8c79 show date after 7 days, closes #754
adjust the behavior of ago() to show the date after a week
2023-05-30 18:22:22 -04:00
Ollie H 234e04c396 Update follower/following a11y hints and labels (#755)
* Update follower/following a11y hints and labels

* Update ProfileHeader.tsx

---------

Co-authored-by: Ansh <anshnanda10@gmail.com>
2023-05-30 13:39:50 -07:00
Ben Harris 09c515d273 fix web links to no longer point to staging (#758)
* fix web link in README.md

* missed one
2023-05-30 13:34:14 -07:00
Josh Soref c808721938 Spelling (#772)
* spelling: account

Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>

* spelling: activated

Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>

* spelling: additional

Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>

* spelling: appropriate

Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>

* spelling: bskyweb

Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>

* spelling: description

Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>

* spelling: display

Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>

* spelling: highlighted

Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>

* spelling: javascript

Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>

* spelling: locally-hosted

Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>

* spelling: notification

Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>

* spelling: occurring

Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>

* spelling: parenthetical

Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>

* spelling: preexisting

Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>

* spelling: prefetched

Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>

* spelling: punctuation

Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>

* spelling: simplicity

Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>

---------

Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-05-30 14:46:43 -05:00
Ansh 724951e2f0 stick to bottom sheet version 4.4.5 (#777) 2023-05-30 14:37:40 -05:00
GitStart 69bcb72061 update delete app password confirmation modal (#803) 2023-05-30 13:56:30 -05:00
Logan Rosen a89129fcf6 Fix button to find people to follow (#806) 2023-05-30 13:55:06 -05:00
Paul Frazee 19ea6b6ae7 Merge branch 'ivansilvestre-main' into main 2023-05-30 13:53:46 -05:00
Paul Frazee e77c616970 Hide both the left and right nav on desktop when logged out 2023-05-30 13:53:33 -05:00
Paul Frazee 7a7b99d033 Merge branch 'main' of https://github.com/ivansilvestre/social-app into ivansilvestre-main 2023-05-30 13:45:44 -05:00
bnewbold a51d5dc7e2 labels: add 'corpse' to 'violent' category (#802)
Overlooked this when the label was added earlier.
2023-05-30 13:43:37 -05:00
Ivan Silvestre caab6309df Desktop: hide the left panel and the invite codes from the main page if you are not logged in 2023-05-30 13:54:23 +01:00
Jake Gold e58fe2e204 add/serve .well-known files (both files) 2023-05-26 17:59:25 -07:00
Jake Gold c98b65f6a9 add/serve .well-known files 2023-05-26 17:49:58 -07:00
Paul Frazee 7b6948e617 Merge branch 'custom-algos' into main 2023-05-25 21:17:11 -05:00
Paul Frazee e832352e98 Bump build numbers 2023-05-25 21:14:45 -05:00
Paul Frazee 62b309be5b Include follow button in posts on the multifeed 2023-05-25 21:13:30 -05:00
Paul Frazee 93984277c9 Simulator fixes 2023-05-25 21:10:44 -05:00
Ansh Nanda bcedbbc8b3 error for custom feed when it is not valid or offlien 2023-05-25 19:01:37 -07:00
Paul Frazee 11262ffc83 Merge branch 'custom-algos' of github.com:bluesky-social/social-app into custom-algos 2023-05-25 20:49:16 -05:00
Paul Frazee 229153a26c Fix tsc 2023-05-25 20:48:43 -05:00
Ansh Nanda fa9b91f793 Merge branch 'custom-algos' of https://github.com/bluesky-social/social-app into custom-algos 2023-05-25 18:48:41 -07:00
Ansh Nanda 4cd9a4493e add empty state for custom feed 2023-05-25 18:48:40 -07:00
Paul Frazee 25153f1f4d Fix lint 2023-05-25 20:45:35 -05:00
Paul Frazee 68332bc877 UI tune ups 2023-05-25 20:44:37 -05:00
Paul Frazee 27ff9ce43b Remove sorting in feed-manip 2023-05-25 20:32:12 -05:00
Paul Frazee 9da5a1a780 Merge branch 'custom-algos' of github.com:bluesky-social/social-app into custom-algos 2023-05-25 20:28:01 -05:00
Paul Frazee 0a1f912ece Fixes & updates for mobile web 2023-05-25 20:27:55 -05:00
Ansh Nanda 2c970781b7 replace profile with hamburger menu in search as well 2023-05-25 18:25:31 -07:00
Paul Frazee 651f3abc1f Tune up custom feed uis on web 2023-05-25 20:19:49 -05:00
Paul Frazee 216d301191 Fix bad import 2023-05-25 20:12:49 -05:00
Paul Frazee 767f5353ea Merge branch 'custom-algos' of github.com:bluesky-social/social-app into custom-algos 2023-05-25 20:02:41 -05:00
Paul Frazee 257686f360 Add feeds tab 2023-05-25 20:02:37 -05:00
Ansh Nanda 202a56cff6 small tweak to tab bar positioning 2023-05-25 16:23:32 -07:00
Paul Frazee df6d249e85 Small tweak to feed tab bar positioning 2023-05-25 17:53:57 -05:00
Paul Frazee dd1bbcfb0b Add scrollview to drawer 2023-05-25 17:26:34 -05:00
Paul Frazee 8fa33ac928 Add my feeds to the drawer 2023-05-25 17:13:32 -05:00
Paul Frazee 7916c2e02e Add 'popular with friends' to default feeds 2023-05-25 17:10:00 -05:00
Paul Frazee bf056cf622 Visually unify the home header with other screens 2023-05-25 17:09:23 -05:00
Paul Frazee cc6ead42d0 Switch to new satellite icon on the header 2023-05-25 17:00:39 -05:00
Paul Frazee 20ef1b8b7b Add custom satellite-dish icons 2023-05-25 16:51:12 -05:00
Paul Frazee 0f30f743cb Use the bars icon on the viewheader 2023-05-25 15:56:29 -05:00
Paul Frazee e33dad8b72 Merge branch 'custom-algos' of github.com:bluesky-social/social-app into custom-algos 2023-05-25 15:56:19 -05:00
Ansh Nanda 881be748d5 increase save custom feed btn hitslop 2023-05-25 13:54:43 -07:00
Ansh Nanda bce6c8d5ec rename Algos to Feeds on profile 2023-05-25 13:51:08 -07:00
Ansh Nanda a9027cfb45 turn of auto correct and capitalize on search 2023-05-25 13:50:59 -07:00
Ansh Nanda 14619cf8a3 fix type errors on Custom Feed screen 2023-05-25 13:46:17 -07:00
Ansh Nanda be2beae1f1 delete old My Feeds / Saved Feeds screen 2023-05-25 13:46:06 -07:00
Ansh Nanda f891305185 fix padding on Discover Feeds screen 2023-05-25 13:42:26 -07:00
Ansh Nanda 8bb3f40a59 add saved feeds to left nav on desktop 2023-05-25 13:14:40 -07:00
Ansh Nanda 6e5078e0c1 Merge branch 'custom-algos' of https://github.com/bluesky-social/social-app into custom-algos 2023-05-25 13:00:16 -07:00
Paul Frazee a39a06ba09 Tune the saved feeds ui 2023-05-25 14:58:04 -05:00
Paul Frazee f03ac9fd56 Tune the UI layout of the feed tab header 2023-05-25 14:56:23 -05:00
Ansh Nanda db63442360 adjust styling for feeds tab bar on mobile 2023-05-25 12:54:31 -07:00
Ansh Nanda 524f8b6abd Merge branch 'custom-algos' of https://github.com/bluesky-social/social-app into custom-algos 2023-05-25 00:18:00 -07:00
Ansh Nanda 0bdb0ac006 put user avatar on bottom bar and replace with hamburger menu on top 2023-05-25 00:17:57 -07:00
Ansh Nanda 007c523010 add top bar and link to new SavedScreen 2023-05-25 00:08:44 -07:00
Paul Frazee 93e9fa8edf Restore the 'load more' button on web 2023-05-25 01:53:37 -05:00
Paul Frazee e89103915d Update load latest to show when there's new activity with an indicator 2023-05-25 01:30:48 -05:00
Paul Frazee 6f02548bca Make load-latest button same on web 2023-05-25 00:20:49 -05:00
Paul Frazee 0262ed11ea Fix the 'dancing header' problem on web 2023-05-25 00:10:48 -05:00
Paul Frazee 5124be33ad Tune the footer text ui a bit 2023-05-24 23:59:37 -05:00
Paul Frazee b00e0d71ef Bump build number for ios 2023-05-24 22:10:14 -05:00
Paul Frazee dfb39e7c4f Add feed discovery page 2023-05-24 22:09:39 -05:00
Paul Frazee 12c7f6d6a5 Tune the custom feeds header a bit more 2023-05-24 19:27:04 -05:00
Paul Frazee 629ca24e90 Tune the custom feed header 2023-05-24 19:03:59 -05:00
Paul Frazee 2ba4d9bfbf Add compose fab to custom feed screen 2023-05-24 18:50:19 -05:00
Paul Frazee 0a18229784 Merge branch 'custom-algos' of github.com:bluesky-social/social-app into custom-algos 2023-05-24 18:46:55 -05:00
Paul Frazee 4e1876fe85 Refactor the scroll-to-top UX 2023-05-24 18:46:27 -05:00
Ansh Nanda 32c9dabb74 make tab bar scroll view draggable on web 2023-05-24 15:04:30 -07:00
Ansh Nanda 7e555ecc1b fix lint errors 2023-05-24 15:00:36 -07:00
Ansh Nanda 04468eb1b7 make prettier and eslint work together 2023-05-24 14:59:42 -07:00
Ansh Nanda 9673225f78 fix scrollToTop for web 2023-05-24 14:18:49 -07:00
Ansh Nanda 58a0489ce3 add isWeb check to disable haptics on web 2023-05-24 13:59:53 -07:00
Ansh Nanda 858ec6438d show scroll to top button when scrolling stops 2023-05-23 15:48:14 -07:00
Ansh Nanda 0fd5c9294a refactor load latest btn 2023-05-23 15:33:27 -07:00
Ansh Nanda fc9e28ca72 slight performance improvements 2023-05-23 15:28:46 -07:00
Ansh Nanda b561a51ed9 add button to reset preferences in dev mode 2023-05-23 14:18:35 -07:00
Ansh Nanda 52a8879754 update pinned feed from custom feed view 2023-05-22 20:07:40 -07:00
Ansh Nanda 8a2349c55f increase pin button hitslop 2023-05-22 19:14:10 -07:00
Ansh Nanda 512c918c03 decrease long press time required to reoreder pinned feed 2023-05-22 19:10:03 -07:00
Ansh Nanda dfcdd37087 add haptics to save, like, and pin actions on feed 2023-05-22 18:46:36 -07:00
Ansh Nanda 64e303d911 optimistic updates for liking custom feeds 2023-05-22 16:35:37 -07:00
Ansh Nanda 762bd15ed6 fix prod default feeds not working 2023-05-22 16:12:05 -07:00
Ansh Nanda 7cad7d12f1 add refreshControl to tab ViewSelector 2023-05-19 18:32:21 -07:00
Ansh Nanda 8bcbbb869a fix dark mode color for creator handle on CustomFeed screen 2023-05-19 18:30:24 -07:00
Ansh Nanda 48a9e1b1dd fix refresh control color in ViewSelector.tsx 2023-05-19 18:27:13 -07:00
Ollie H 15c1b6ee15 Merge pull request #744 from bluesky-social/ollie/693
Update labels for avatar
2023-05-18 21:09:39 -07:00
Paul Frazee 91ae0d7775 1.29 testflight 2023-05-18 21:06:00 -05:00
renahlee 2a5ac1a6de Update labels for avatar 2023-05-18 17:31:57 -07:00
Paul Frazee 37acc9e930 A few more UX tweaks 2023-05-18 18:22:46 -05:00
Paul Frazee 0b8dd95f2a Bump deps again 2023-05-18 18:22:34 -05:00
Paul Frazee da6fa2dede Bump deps 2023-05-18 18:04:32 -05:00
Paul Frazee 4fa4c67cc5 Some fixes 2023-05-18 17:36:16 -05:00
Paul Frazee 46ed910cda Add list-type avatar 2023-05-18 17:10:42 -05:00
Paul Frazee 324c9209dc Only show algos and lists on profiles if there are items 2023-05-18 17:01:48 -05:00
Paul Frazee ad8778ab10 Add server-side routes 2023-05-18 16:55:50 -05:00
Paul Frazee 3c89dd40f9 Fix lint 2023-05-18 16:54:17 -05:00
Paul Frazee 1ecf0da81b Add feed sharing 2023-05-18 16:22:11 -05:00
Paul Frazee 84990c509e Drop the hard-coded what's hot algo 2023-05-18 15:12:18 -05:00
Paul Frazee 2f4408582b Set default feeds 2023-05-18 15:06:32 -05:00
Paul Frazee 9c9e4e5691 Merge branch 'custom-algos' of github.com:bluesky-social/social-app into custom-algos 2023-05-18 14:39:12 -05:00
Paul Frazee 5537d19e55 Update saved feeds to use preferences 2023-05-18 14:39:04 -05:00
Jake Gold 211fce47ce Fix /api/waitlist API (#726)
* move /waitlist to /api/waitlist where its expected

* parse waitlist API request as JSON, duh
2023-05-18 11:56:09 -07:00
Ansh Nanda f1d2166c29 fix spacing when user has no feeds 2023-05-18 10:36:16 -07:00
Ansh Nanda 571fc37a99 fix error & empty state when rendering custom feeds on profile 2023-05-18 10:34:34 -07:00
Paul Frazee acea0e074d Tab bar fixes 2023-05-18 12:05:32 -05:00
Paul Frazee 754496b957 Fix custom feed header on desktop web 2023-05-18 11:57:49 -05:00
Paul Frazee 7691fe4f48 Store/sync pinned feeds on the server 2023-05-18 11:51:25 -05:00
Paul Frazee d88c27a419 Improve web titles 2023-05-17 23:36:36 -05:00
Paul Frazee 5a20e0fafa Add web titles 2023-05-17 23:33:59 -05:00
Paul Frazee 129fc42e95 Improve pinned feed management 2023-05-17 23:32:19 -05:00
Jaz 7f76c2d67e Merge branch 'main' into inherit_system_theme 2023-05-17 21:19:20 -07:00
Jaz ac3a95dc72 Remove unused imports 2023-05-17 21:15:37 -07:00
Jaz 3c15f6ba02 Move appearance settings to settings page 2023-05-17 21:14:26 -07:00
Paul Frazee bec94ed82c Desktop styling fixes 2023-05-17 22:58:53 -05:00
Paul Frazee ed7293ea66 Remove redundant component 2023-05-17 22:15:44 -05:00
Paul Frazee a2c89b47a1 Add custom feed liked by screen 2023-05-17 22:12:14 -05:00
Paul Frazee 998879d6d6 Remove redundant feed-settings page 2023-05-17 22:04:01 -05:00
Paul Frazee 177df36330 Fixes to the tab bar 2023-05-17 21:54:40 -05:00
Paul Frazee 6bf8e72157 Usability improvements to feeds 2023-05-17 21:23:32 -05:00
Paul Frazee f0003d1931 Add 'my feeds' tab 2023-05-17 20:33:58 -05:00
Paul Frazee c55ce6de02 Rework the UI for the custom feed view 2023-05-17 15:52:11 -05:00
Paul Frazee 0f5b3808f8 Remove a focus-effect that was triggering a rerender loop 2023-05-17 15:03:19 -05:00
Paul Frazee 2fd3b4ca04 Clean up the custom-feed model 2023-05-17 14:55:04 -05:00
Paul Frazee 9c02fbb925 Unify the display name fallback behavior 2023-05-17 14:38:23 -05:00
Paul Frazee 4e56d24a9d Dont show save button in embed cards 2023-05-17 14:34:37 -05:00
Paul Frazee fb03bbf288 Fix some palette issues 2023-05-17 14:26:23 -05:00
Paul Frazee a4c6dd6b1e Tune up the custom feed card rendering 2023-05-17 14:24:27 -05:00
Paul Frazee 3d09008bfd Reorganize custom feeds in the view layer 2023-05-17 14:03:08 -05:00
Paul Frazee b672006f7e Reorganize custom-feed state models and add the missing _reactKey attribute 2023-05-17 13:52:16 -05:00
Paul Frazee a2b089d315 Add 'algo' variant of avatars 2023-05-17 13:42:29 -05:00
Paul Frazee 85910cdbea Fix missed change in merge 2023-05-17 13:42:16 -05:00
Paul Frazee df3b988157 Update yarn.lock 2023-05-17 13:42:09 -05:00
Paul Frazee 7aa1d9010e Merge branch 'main' into custom-algos 2023-05-17 12:30:54 -05:00
Ollie H 0ca096138a Merge pull request #692 from bluesky-social/ollie/unread-notifications
Update hint for notifications
2023-05-17 09:37:54 -07:00
renahlee d6ca8e918a Update to empty hint if no notifications 2023-05-17 09:13:06 -07:00
renahlee f7f0db65d3 Update hint for notifications 2023-05-17 09:12:09 -07:00
Paul Frazee 562faa76cc feat: add basic visual feedback on Button interactions (#708)
* feat: add basic visual feedback on Button interaction

* Use a more efficient approach to providing visual feedback

* Fix some lint issues left over from another PR

---------

Co-authored-by: Simek <gosimek@gmail.com>
2023-05-17 11:10:51 -05:00
LW 41f3a05515 style: remove useUnreadCountLabel hack from 50c1841 (#655) (#686)
I just realized how `mobx` works (never used before lol) and now I feel
dumb.
2023-05-17 09:50:28 -05:00
Ollie H 656baa7239 Close active elements on state change (#677) 2023-05-17 09:46:10 -05:00
Richard Gordon 09bb2f5208 hidereport button on own post (#676) 2023-05-17 09:43:08 -05:00
William d7826a3334 Properly set isInputFocused (#666) 2023-05-17 09:41:54 -05:00
Mohammad Reza Mahmoudi 6dde5ede34 fix theme opposite labels (#700) 2023-05-17 09:33:05 -05:00
Pedro b132ff7a76 Update build.md - minor typo (#696)
This is not the smallest PR in the world, this is just a tribute.

(actually, it's just one byte, so it might be the smallest PR in the world)
2023-05-17 09:27:02 -05:00
LW b1c5794190 docs: update e2e mock server command (#662) 2023-05-17 09:21:34 -05:00
Matin Zadeh Dolatabad c1fe12f5bf chore: update expo-image to 1.2.3 (#664)
Issue: https://github.com/expo/expo/pull/22491
2023-05-17 09:19:59 -05:00
Ansh Nanda 52c72d6532 cleanup package.json 2023-05-16 23:46:29 -07:00
Ansh Nanda 90adc19415 fix re-rendering issue 2023-05-16 23:45:14 -07:00
Ansh Nanda 1378ab46e9 make sure TabBar re-rerenders when pinned feeds changes 2023-05-16 23:40:51 -07:00
Ansh Nanda 3501fda015 allow changing pinned feed order on web 2023-05-16 23:38:34 -07:00
Jaz b2ef6bde00 Unstage open post in new tab changes, oops 2023-05-16 23:22:18 -07:00
Jaz f4327aff69 Fix mobile layout at least for mobile web 2023-05-16 23:11:29 -07:00
Jaz 85dfef87ab Use a three-state radio button for color mode 2023-05-16 23:06:08 -07:00
Jaz da0ed7e002 Feat: Use system default color mode, but allow user override 2023-05-16 21:36:43 -07:00
Ansh Nanda 99d66679b3 use uri instead of rkey to get feed data 2023-05-16 19:09:19 -07:00
Ansh Nanda 59c1b545fa fix routing on web 2023-05-16 19:04:47 -07:00
Ansh Nanda ff3cc3b84d fix dark mode styling 2023-05-16 18:39:02 -07:00
Ansh Nanda 53ca0cd626 drag to rearrange pinned items 2023-05-16 18:28:44 -07:00
Ansh Nanda 139027ac5f bad fix to the tab bar animation 2023-05-16 17:27:46 -07:00
Jacques Favreau 404b2f043c fix: Add vendor prefix for web blur. (#678) 2023-05-16 18:39:15 -05:00
Michi Hoffmann b7f081d6e4 Fix line break in New Post button (#657) 2023-05-16 18:38:06 -05:00
Ansh Nanda 532bc88ecc fix edge case when feed is liked only by one person 2023-05-16 16:31:35 -07:00
Ansh Nanda 5397447ca7 fix unliking 2023-05-16 16:30:14 -07:00
Ansh Nanda 1e54125f7f reload AlgoItem on focus via prop 2023-05-16 16:27:38 -07:00
Ansh Nanda 3f41d3db26 make sure state is being synced across components 2023-05-16 16:07:07 -07:00
Ansh Nanda f2e39d8ad2 allow for pinning saved feeds 2023-05-16 15:50:52 -07:00
Ollie H 5f66adc9a6 Replace image picker with expo-image-picker (#649)
* Replace image picker with expo-image-picker

* Fix cropper & picker on web

---------

Co-authored-by: Paul Frazee <pfrazee@gmail.com>
2023-05-16 17:38:32 -05:00
Ansh Nanda dd788550be add heart icon to AlgoItem 2023-05-16 13:57:29 -07:00
Ansh Nanda 58e9c30abe link to user who created custom feed from preview 2023-05-16 13:56:59 -07:00
Ansh Nanda 25a9013380 add react-native-draggable-flatlist package 2023-05-16 13:41:14 -07:00
Ansh d5bec4ff37 Update build instructions (#639)
* Create .env.example

* Update build.md

* Update eas.json

* Update build.md

* Update build.md

* Update build.md

* Update docs/build.md

Co-authored-by: Caleb Jasik <calebjasik@jasik.xyz>

---------

Co-authored-by: Caleb Jasik <calebjasik@jasik.xyz>
2023-05-16 13:14:02 -05:00
LW 50c1841a06 feat: Update HTML title on web #626 #599 (#655)
For any `Screen` that shows on desktop, `title` is "(1) ... - Bluesky"
where "(1)" is the unread notification count.

The titles are unlocalized and the string "Bluesky" is hardcoded,
following the pattern of the rest of the app.

Display names and post content are loaded into the title as effects.

Tested:
* all screens
* screen changes / component mounts/unmounts
* long posts with links and images
* display name set/unset
* spamming myself with notifications, clearing notifications
* /profile/did:... links
* lint (only my changed files), jest, e2e.

New utilities: `useUnreadCountLabel`, `bskyTitle`,
`combinedDisplayName`, `useSetTitle`.

resolves: #626 #599
2023-05-16 13:13:05 -05:00
LW a5838694bd test: detox needs tapReturnKey after typeText (#663)
Without this, all `yarn e2e:run` tests get hung on the "input custom
service" step when logging in.

Only used one machine to repro, so an additional repro could be useful.
2023-05-16 12:48:54 -05:00
Markus Amalthea Magnuson 924e18e644 Remove surrounding spaces in parentheses. (#667) 2023-05-16 12:44:30 -05:00
Ansh Nanda 8071ae313d like/unlike improvements WIP 2023-05-15 22:39:47 -07:00
Ansh Nanda c4a666c221 new design for custom feed preview 2023-05-15 17:59:36 -07:00
Ollie H a891f4c326 Merge pull request #631 from aarongustafson/patch-1
Remove max scale from viewport config `meta`
2023-05-15 17:07:44 -07:00
Aaron Gustafson 255bba5fb8 Remove max scale from viewport config meta
Maximum scale creates accessibility issues for folks who need to zoom in
2023-05-15 15:44:10 -07:00
Ollie H e2055dfb78 Image editor mobile layout update (#613)
* Image editor mobile layout update

* Minor viewport fix
2023-05-15 16:54:14 -05:00
Tierney Cyren aa786068cf Create LICENSE file and point to it from README.md (#623)
* Create LICENSE

adds a LICENSE file that can be picked up by first- and third-party tooling

* Update README.md to point to LICENSE file
2023-05-15 16:02:41 -05:00
Paul Frazee 952af3473a Prepare for open-sourcing (#619)
* Add issue templates

* Update readme and docs for contributors

* Tune up the README.md

* Tune language a bit

* Tune copy
2023-05-15 15:45:52 -05:00
Paul Frazee 5e8e0ad680 Update the default suggested users list to match recent activity 2023-05-15 15:27:58 -05:00
Ollie H 628d877325 Use dropdown for web reposting and quote posting (#607)
* Use dropdown for web reposting and quote posting

* Remove collateral damage

* Tune the repost dropdown positioning

* Move postctrls into their own folder

* Factor out repost button into native/web build

---------

Co-authored-by: Paul Frazee <pfrazee@gmail.com>
2023-05-15 15:18:39 -05:00
Ansh Nanda 6249bb16ca remove tab bar underline animation 2023-05-15 12:31:27 -07:00
bnewbold 0a0afdf2c2 reporting: anti-social option; alert emoji around urgent option (#617)
* reporting: new anti-social post report option

* reporting: alarm emoji on urgent report option

* reporting: appease 'make lint'

I originally had text on a separate line, but that failed, so I put
text+tags on a line and thought that worked, but failed again? Seems
inconsistent, but trying again.

* Remove emojis and increase report post height

---------

Co-authored-by: Paul Frazee <pfrazee@gmail.com>
2023-05-15 14:25:50 -05:00
Ollie H 824a2313b1 Style cleaning and initial text size audit (#618)
* Style cleaning and initial text size audit

* Put file back
2023-05-15 13:14:45 -05:00
Ollie H 841ad692d3 Make autocomplete pressable (#620) 2023-05-15 13:12:38 -05:00
Ansh Nanda d7e39bde12 saved feeds in tab bar 2023-05-15 11:00:54 -07:00
Ansh Nanda 5010861160 custom feed screen 2023-05-15 10:42:23 -07:00
Ansh Nanda 61ea37ff81 renamed page to savedfeeds 2023-05-14 18:37:18 -07:00
Ansh Nanda 6105314f15 add empty view to CustomAlgorithms screen 2023-05-14 15:38:38 -07:00
Ansh Nanda 8948118d5c view all saved algos in settings 2023-05-13 12:25:06 -07:00
Ansh Nanda 047024a5ac create algo-item model and redefine data models 2023-05-13 11:59:08 -07:00
Ansh Nanda c24389df87 custom feed embed 2023-05-12 19:46:50 -07:00
Ansh Nanda 760b5309e0 show algos by user on profile 2023-05-12 19:32:39 -07:00
Ansh Nanda fa4af20764 remove unncessary code from actorFeedModel 2023-05-12 17:22:53 -07:00
Ansh Nanda 06ce42158e actor feeds model 2023-05-12 17:21:17 -07:00
Ansh Nanda be6d48a57e bookmarked feeds model 2023-05-12 17:21:11 -07:00
Ansh Nanda 370d52bd1f add custom algorithm screen to settings under moderation 2023-05-12 14:40:58 -07:00
Ansh Nanda 678f75b495 add window dimensions to global styles 2023-05-12 13:51:07 -07:00
Paul Frazee 5f074a21c9 Tune hand icon 2023-05-12 10:21:09 -05:00
Paul Frazee 0a7bb43644 Replace the moderation hand icon with a better icon 2023-05-12 10:10:29 -05:00
Paul Frazee 60cfdb197c Bump version numbers 2023-05-11 18:14:32 -05:00
Paul Frazee 6a37043801 Tests fix 2023-05-11 18:13:50 -05:00
Paul Frazee 75007d8fae [APP-643] Account preferences server sync (#615)
* Bump deps

* Bump deps

* Add server sync of content preferences and an adult content toggle
2023-05-11 17:52:38 -05:00
Ollie H c2a8713ff4 a11y cleanup for mute lists (#614) 2023-05-11 16:30:27 -05:00
Paul Frazee ebcd633386 [APP-635] Mutelists (#601)
* Add lists and profilelist screens

* Implement lists screen and lists-list in profiles

* Add empty states to the lists screen

* Switch (mostly) from blocklists to mutelists

* Rework: create a new moderation screen and move everything related under it

* Fix moderation screen on desktop web

* Tune the empty state code

* Change content moderation modal to content filtering

* Add CreateMuteList modal

* Implement mutelist creation

* Add lists listings

* Add the ability to create new mutelists

* Add 'add to list' tool

* Satisfy the hashtag hyphen haters

* Add update/delete/subscribe/unsubscribe to lists

* Show which list caused a mute

* Add list un/subscribe

* Add the mute override when viewing a profile's posts

* Update to latest backend

* Add simulation tests and tune some behaviors

* Fix lint

* Bump deps

* Fix list refresh after creation

* Mute list subscriptions -> Mute lists
2023-05-11 16:08:21 -05:00
bnewbold 34d8fa5991 top-level Makefile (#597)
* top-level Makefile

The primary motivation here is the `build-web` command, which calls the
yarn build and then also copies over JS files. The Dockerfile does this
and I always forget when doing it manually.

* build-web: cp bundles in yarn command, not Makefile+Dockerfile
2023-05-11 13:22:56 -07:00
bnewbold d624b7cf58 bskyweb: iterate on HTML card metadata (#609)
Probably still not perfect, but better.

- don't user avatar image. use banner for profile and post img, or
  nothing
- most twitter metadata fields were redundant; twitter will parse out
  opengraph ("og:"), so don't duplicate those
- add regular HTML description (for google, etc)
- include URI
- actually include text
2023-05-11 10:41:47 -05:00
Ollie H 19d6ded631 Prevent reply to from cascading (#610) 2023-05-11 10:38:54 -05:00
Ollie H 0192923ef3 Only allow one close draft confirmation at a time (#611)
* Only allow one close draft confirmation at a time

* lint
2023-05-11 10:38:10 -05:00
bnewbold c2fb4d4b4b small label updates (#612)
* labels: fix 'consentual' typo

This label has never been applied, so safe to just change it.

* labels: new 'nsfl' label, under both porn and gore categories

* labels: new account-security label (always warn)

* labling: re-word spam subtitle
2023-05-11 10:37:26 -05:00
Ollie H b0ebb6c9d1 Update web image editor (#588)
* Update web image editor

* Delete type-assertions.ts

* Re-add getKeys

* Uncomment rotation code

* Revert "Uncomment rotation code"

This reverts commit 6269f3b928.

* Shuffle dependencies and update mobile resolution

* Update ImageEditor modal layout for mobile

* Avoid accidental closes of the EditImage modal

---------

Co-authored-by: Paul Frazee <pfrazee@gmail.com>
2023-05-09 14:55:44 -05:00
Ollie H 8f6b5d3df9 Add avatar to mobile autocomplete and create grapheme hook (#602)
* Add avatar to mobile autocomplete and create grapheme hook

* Remove comment, update filename, cut out redundant logic
2023-05-09 12:13:23 -05:00
bnewbold 9a91b0c538 bskyweb: middleware to remove trailing / (#598) 2023-05-09 12:03:42 -05:00
Ollie H bf3ea67442 Add time to app password and fix related text wrapping (#604) 2023-05-09 12:02:55 -05:00
Ansh 28f7ff76a4 add target="_blank" prop to LinkText for safari (#606) 2023-05-09 12:01:42 -05:00
Ollie H cfdfd8f395 Add text wrapping to profile header (#603) 2023-05-09 12:00:54 -05:00
Paul Frazee d0990e9b49 Bump ios build number 2023-05-09 00:45:33 -05:00
Paul Frazee b756a27958 [APP-639] Improve nsfw handling & force hidden on iOS (#605)
* Identify adult content labels and handle them more specifically

* Change adult content defaults to more conservative settings

* Add an adultcontentenabled override that prohibits access on iOS

* Improve usability of the content hider

* Fix lint
2023-05-09 00:43:20 -05:00
Paul Frazee 7a176b3fdf [APP-615] COPPA-compliant signup (#570)
* Rework account creation to be COPPA compliant

* Fix lint

* Switch android datepicker to use the spinner mode

* Fix type signatures & usages
2023-05-08 17:25:57 -05:00
Ollie H cdfb1c7abf Visual improvements to web autocomplete (#591)
* Visual improvements to web autocomplete

* Remove stray styling
2023-05-08 16:09:15 -05:00
Ollie H 84046f42d5 Format count and fix type warning (#600) 2023-05-08 16:02:19 -05:00
Ansh 6124e52836 add theme to SafeAreaView and preventAutoHide SplashScreen (#592) 2023-05-08 15:05:36 -05:00
bnewbold 249d166811 bskyweb: re-work HTML meta tags, for share cards (#596) 2023-05-08 15:03:03 -05:00
bnewbold 6d9e23b1be bskyweb: update robots.txt (#595)
This is to make crawling more explicitly allowed, communicating
expectations.

If we ever end up with "expensive" routes on this service, will want to
add Crawl-Delay.
2023-05-07 12:19:56 -07:00
Ollie H 0c604ff1c2 Merge pull request #593 from bluesky-social/ollie/fix-hardbreak
Fix hardbreak formatting
2023-05-05 16:53:12 -07:00
renahlee 2f61fc31fd Fix hardbreak formatting 2023-05-05 16:36:48 -07:00
Paul Frazee b3b1fa1707 1.28 2023-05-05 00:55:33 -05:00
Paul Frazee ce89b2fae1 Add account information to the top of settings (#583) 2023-05-05 00:41:31 -05:00
Ansh 646eddad5a make haptic feedback weaker on android (#584) 2023-05-05 00:41:08 -05:00
Ansh 99ecb17739 useLegacyImplementation in Android (#585) 2023-05-05 00:40:16 -05:00
Ansh f28405f928 sanitize app passwords name (#589) 2023-05-05 00:27:05 -05:00
Ollie H 8d78e8581c Move href back to link (#590)
* Move href back to link

* Fix cmd/ctrl click on left nav

---------

Co-authored-by: Paul Frazee <pfrazee@gmail.com>
2023-05-05 00:25:52 -05:00
Ansh d3e8bd3e9a [APP-547] Universal links & deeplinking (#555)
* added ios scheme and intentFilters for deep linking

* added intentFilters for android deep linking

* add .env files to .gitignore

* add autoVerify for android deep links
2023-05-04 16:18:27 -05:00
bnewbold c8af784328 Updates indigo golang packages post-lex-refactor (!), which fixes XRPC calls (#582)
* bskyweb: update modules

* bskyweb: fix XRPC string type

* gitignore: yarn web build output in bskyweb
2023-05-04 13:56:17 -05:00
Paul Frazee 7a008c987c 1.27 2023-05-04 01:21:08 -05:00
Paul Frazee 49f9df6355 [APP-633] Improve some behaviors around desktop leftnav (#581)
* Make leftnav elements act as anchor tags (bonus feature in this pr)

* Add screen reset behavior to the desktop left nav

* Move the leftnav link into the text
2023-05-04 01:20:23 -05:00
Paul Frazee 2749b8e371 Rework alt image modal to be fullscreen due to android bugs with the bottomsheet and keyboard (#577) 2023-05-04 00:55:57 -05:00
Paul Frazee ab3074fdee Add the !filter and !warn imperative labels (#580) 2023-05-04 00:55:33 -05:00
Ollie H 33bf9c3869 Remove focus outline on composer (#572) 2023-05-04 00:54:59 -05:00
Paul Frazee d97e75c62f [APP-539] Rework lightbox and alt-image behaviors (#573)
* Replace the long press on the lightbox with footer controls

* Remove long-press from images in the feed

* Tune the lightbox footer control ui

* Replace the AltImageRead modal with the ability to view all alt text in the lightbox footer

* Tune lightbox footer for iOS

* Add alt text to the web lightbox

* Fix lint

* a11y slight changes

---------

Co-authored-by: renahlee <renahlee@outlook.com>
2023-05-04 00:54:35 -05:00
Ollie H 4ef853ef6c Remove text underline from lists (#574) 2023-05-04 00:54:22 -05:00
Ollie H 011baa78c1 a11y label cleanup (#576) 2023-05-04 00:53:49 -05:00
Paul Frazee 0f68e6a6ff Add mock data to test unknown labels (#578) 2023-05-04 00:24:14 -05:00
Paul Frazee 7f88845c9b Add icon-intolerant and behavior-intolerant to the political hategroup category (#579) 2023-05-04 00:22:54 -05:00
Ollie H e2d2bbb0d5 Merge pull request #575 from bluesky-social/ollie/README
Update README with Go instructions
2023-05-03 20:47:04 -07:00
Ollie H aed54822a3 Update README.md 2023-05-03 20:39:49 -07:00
renahlee 13586ec6ef Update README with Go instructions 2023-05-03 20:20:52 -07:00
Paul Frazee 906b906eb1 Remove the attempts to make the language filter more lenient (#569) 2023-05-03 11:49:46 -05:00
Jake Gold 204c6729e7 add required security HTTP headers (#568) 2023-05-03 07:14:29 -07:00
Paul Frazee 14e9719bcc Increment android versioncode 2023-05-03 00:45:19 -05:00
Paul Frazee feff19f705 1.26 2023-05-02 23:56:01 -05:00
Paul Frazee 12a2144232 Remove some confusing horizontal lines in threads (#566) 2023-05-02 23:32:29 -05:00
Paul Frazee 883700e090 [APP-601] Add muted accounts list (#565)
* Add muted accounts list

* Fix icon for muted accounts
2023-05-02 23:32:16 -05:00
Paul Frazee 2eb0d8c095 [APP-611] Add nice date to expanded post view (#567)
* Add nice date to expanded post view

* Fix styles
2023-05-02 23:29:16 -05:00
Paul Frazee 6f1c4ec9a9 [APP-549] Language controls for Whats Hot (#563)
* Add a content-language preference control

* Update whats hot to only show the selected languages and to refresh on lang pref changes

* Fix lint

* Fix tests

* Add missing accessibility role
2023-05-02 23:06:55 -05:00
Ollie H 95f8360d19 Add keyboard shortcuts: new, escape, and hard break (#552)
* Add keyboard shortcuts: new, escape, and hard break

* Add preferences modal

* Remove code accidentally re-added due to rebase

* Fix incorrect copy and lint

* Put stuff back so diffs are clearer

* Re-add invite codes to settings

* Address comments

* Tune the copy

---------

Co-authored-by: Paul Frazee <pfrazee@gmail.com>
2023-05-02 23:00:18 -05:00
Paul Frazee af905947bc Fix confirm profile sizing for blocks (#564) 2023-05-02 22:52:58 -05:00
Paul Frazee 8c675248d4 Fix replies with <2 likes showing in the following feed 2023-05-02 21:51:25 -05:00
Paul Frazee d225e857b5 [APP-610] Make the language filter more lenient (#562)
* Tune the language filter to accept posts when a determination cant be made

* use j instead of i since i has been declared in upper scope

* use j instead of i since i has been declared in upper scope

* Pass the j man

---------

Co-authored-by: Ansh Nanda <anshnanda10@gmail.com>
2023-05-02 20:03:01 -05:00
Paul Frazee ddb8ebb412 Fix image sharing on iOS (#561) 2023-05-02 20:00:22 -05:00
Ollie H bd80db619b Pre-web image changes refactor (#560)
* Pre-web image changes refactor

* Remove unneeded async behavior
2023-05-02 16:27:00 -05:00
Ollie H 74fbb47979 Strip whitespaces from tokens (#558) 2023-05-01 23:29:13 -05:00
Ollie H 83959c595d React Native accessibility (#539)
* React Native accessibility

* First round of changes

* Latest update

* Checkpoint

* Wrap up

* Lint

* Remove unhelpful image hints

* Fix navigation

* Fix rebase and lint

* Mitigate an known issue with the password entry in login

* Fix composer dismiss

* Remove focus on input elements for web

* Remove i and npm

* pls work

* Remove stray declaration

* Regenerate yarn.lock

---------

Co-authored-by: Paul Frazee <pfrazee@gmail.com>
2023-05-01 20:38:47 -05:00
Ansh c75c888de2 [APP-527] setup sentry (#532)
* setup sentry

* add sentry to transformIgnorePatterns to fix jest issues

* update README with sourcemap instructions

* only enable integrations on native

* fix sentry web

* remove testing code

* fix sentry authToken

* Switch over to paul's auth tokens temporarily (lol)

---------

Co-authored-by: Paul Frazee <pfrazee@gmail.com>
2023-05-01 14:42:31 -05:00
Ollie H dbb3c5c155 Image alt text view modal (#551)
* Image alt text view modal

* Minor style tweaks

---------

Co-authored-by: Paul Frazee <pfrazee@gmail.com>
2023-05-01 13:59:17 -05:00
Ollie H 0ec98c77ef Format large numbers (#556) 2023-05-01 13:31:00 -05:00
Paul Frazee 7171d0404e App store fixes (permission descriptions) (#557)
* Fix permission usage descriptions on iOS

* Bump ios build number
2023-05-01 13:13:58 -05:00
Paul Frazee 9b50c3ddbb 1.25 2023-04-28 20:03:55 -05:00
Paul Frazee a95c03e280 Implement blocks (#554)
* Quick fix to prompt

* Add blocked accounts screen

* Add blocking tools to profile

* Blur avis/banners of blocked users

* Factor blocking state into moderation dsl

* Filter post slices from the feed if any are hidden

* Handle various block UIs

* Filter in the client on blockedBy

* Implement block list

* Fix some copy

* Bump deps

* Fix lint
2023-04-28 20:03:13 -05:00
Ollie Hsieh e68aa75429 Merge pull request #553 from bluesky-social/ollie/APP-581
Up Android snap point
2023-04-28 17:20:33 -07:00
renahlee b754ed0e5a Up Android snap point 2023-04-28 17:12:17 -07:00
Paul Frazee 173e06f866 Fix to embed labels lookup (#550)
* Fix to embed labels lookup

* Fix lint

* Fix lint
2023-04-27 15:01:38 -05:00
Paul Frazee 301c2e5beb Merge branch 'main' of github.com:bluesky-social/social-app into main 2023-04-27 13:15:04 -05:00
Paul Frazee da06b608f2 1.23.0 build 2 2023-04-27 13:14:54 -05:00
Paul Frazee 1d50ddb378 Refactor moderation to apply to accounts, profiles, and posts correctly (#548)
* Add ScreenHider component

* Add blur attribute to UserAvatar and UserBanner

* Remove dead suggested posts component and model

* Bump @atproto/api@0.2.10

* Rework moderation tooling to give a more precise DSL

* Add label mocks

* Apply finer grained moderation controls

* Refactor ProfileCard to just take the profile object

* Apply moderation to user listings and banner

* Apply moderation to notifications

* Fix lint

* Tune avatar & banner blur settings per platform

* 1.24
2023-04-27 12:38:23 -05:00
Ollie Hsieh 51be8474db Update invite code copy (#549) 2023-04-27 12:31:03 -05:00
Ollie Hsieh 7a2c21026d Load previous state in alt text modal (#546) 2023-04-27 09:51:47 -05:00
Ollie Hsieh c8e51a7d48 Fix Android sharing (#545) 2023-04-27 09:38:28 -05:00
Ollie Hsieh 62b07f93fd Support Ctrl + Enter for non-Mac (#544) 2023-04-27 09:31:14 -05:00
Ollie Hsieh 996dba7595 Close lightbox on web with escape key (#543)
* Close lightbox on web with escape key

* Lint
2023-04-27 09:30:47 -05:00
Ollie Hsieh afd87a6390 Add undo to web composer (#542) 2023-04-27 09:27:33 -05:00
Paul Frazee a81158bd2a Tune some copy 2023-04-26 09:33:56 -05:00
Paul Frazee 8d8b3f53a0 1.23 2023-04-25 21:26:33 -05:00
Paul Frazee c8541a7243 Fix e2e 2023-04-25 21:25:49 -05:00
Paul Frazee fc19ffba38 Update report modal to use new groupings (close [APP-567]) (#533) 2023-04-25 21:04:50 -05:00
Paul Frazee 9b86cb5c36 Fix: dont request more than 25 posts at a time (close [APP-561]) (#534) 2023-04-25 20:47:37 -05:00
Paul Frazee f33a355a1a [APP-562] Persist 'copied' state of invite codes (#535)
* Persist 'copied' state of invite codes (close APP-562)

* Dont show copied message if invite used
2023-04-25 20:47:07 -05:00
Paul Frazee e1fd50d014 Add web network failure error detection (close APP-83) (#537) 2023-04-25 20:46:47 -05:00
Ansh e99c324f14 add pal.text to onboarding screens (#538) 2023-04-25 20:46:34 -05:00
Paul Frazee ae895155fd Fix to error screen and postthread loading state (#540)
* Fix loading state on postthread

* Improve error screen rendering

* Dark mode exclamation in error screen

* Fix lint
2023-04-25 20:45:59 -05:00
Ollie Hsieh 8f3915e0a5 Merge pull request #541 from bluesky-social/ansh/app-569-make-sure-android-is-not-allowing-more
[APP-569] Only allow selecting 4 pics max
2023-04-25 14:52:53 -07:00
Ansh Nanda fa612fade6 only allow selecting 4 images max 2023-04-25 14:45:41 -07:00
Ansh c8a7f27d43 Merge pull request #536 from bluesky-social/ansh/app-558-share-profile-and-post-broken-on-android
[APP-558] Sharing refactor
2023-04-25 14:04:56 -07:00
Ansh Nanda d0d24ea248 consolidated share code to shareUrl 2023-04-25 13:45:46 -07:00
Ansh 01410ad4bf create Alert.tsx and Alert.web.tsx and replace uses (#513) 2023-04-25 14:01:47 -05:00
Ansh Nanda 3463f601a3 add dev menu opening instructions by platform 2023-04-25 11:59:11 -07:00
Ollie Hsieh b00365c196 Prevent keyboard from blocking alt image modal (#531)
* Prevent keyboard from blocking alt image modal

* Tune padding of altimage modal on desktop web

---------

Co-authored-by: Paul Frazee <pfrazee@gmail.com>
2023-04-25 13:34:06 -05:00
Paul Frazee 3ef7504f91 1.22 2023-04-24 20:08:37 -05:00
Paul Frazee 26cef73b5c If adding the user's post when empty, just refresh (#530) 2023-04-24 20:07:58 -05:00
Paul Frazee f4da2f4442 Tune feed loading behavior (#528)
* Never autoload home feed on focus

* On web, just check for new notifications on focus

* Switching tab in the home feed now checks for latest
2023-04-24 19:41:27 -05:00
Paul Frazee 7a10762716 Rework how recently-created posts are added to the feed (repeat posts issue) (#527)
* Rework new-post behavior to just add the user's created post to the top

* Only add post to top when not a reply

* Fix: run update in action
2023-04-24 19:41:16 -05:00
Paul Frazee df1791bde2 Update hardcoded suggested actors (#524)
* Update the hardcoded suggested follows

* Update the suggested actors to just use hardcoded list since there's now the foaf system
2023-04-24 18:49:11 -05:00
Ansh c5222db38b memoize things, clean code, and replace deprecated resizeMode with contentFit (#526) 2023-04-24 18:45:02 -05:00
Paul Frazee 1b356556c9 Performance fixes with new getPosts (#525)
* Update notifications to fetch in a batch using getPosts

* Improve search perf with getPosts

* Bump @atproto/api@0.2.9

* Just use post uri for key
2023-04-24 17:02:58 -05:00
Paul Frazee da8af38dcc Android & visual fixes: color themes, repost icon, navigation, back handler, etc (#519)
* Switch android to use slide left/right animations on navigation

* Bump the repost icon down by a pixel

* Tune theme colors for contrast and darker bg on darkmode

* Move back handler to a point in the init flow that leads to more consistent capture of events

* Fix image share flow on android

* Fix lint

* Add todo about sharing not available

* Drop the android slide animation because it's too slow

* Fix 'flashes of white' in dark mode android
2023-04-24 16:36:05 -05:00
Ollie Hsieh 9d8600c213 Merge pull request #522 from bluesky-social/ollie/staging-link
Update client staging URL
2023-04-24 11:06:05 -07:00
renahlee 57c87ba7cf Update client staging URL 2023-04-24 10:55:35 -07:00
Ollie Hsieh df8059bf9a Increase alt text limit to 1000 (#521) 2023-04-24 12:44:42 -05:00
Paul Frazee 36eca53b87 1.21 2023-04-22 19:29:54 -05:00
Paul Frazee c7600fe0c2 Web fixes (#517)
* Fix scroll behaviors on web

* Remove headers on web to avoid scroll overflow

* Fix follow button press in cards
2023-04-22 19:08:41 -05:00
Paul Frazee 5085861b9a Feed fixes: repeat posts & nonreplies showing up in the wrong place (#516)
* Fix the repeat posts issue on the home feed

* Fix: replies no longer show up in the no-replies feed
2023-04-22 18:35:12 -05:00
Paul Frazee d35f7c1f1a Android fixes (#515)
* Fix profile screen performance on android and remove dead code

* Correctly handle android hardware back btn

* Fix EditProfile modal for android

* Fix lint
2023-04-22 17:14:20 -05:00
Ollie Hsieh eb6b36be61 Merge pull request #514 from bluesky-social/ollie/ap-dark-mode
Fix App Passwords iOS dark mode modal
2023-04-22 06:22:08 -07:00
renahlee 7d43e25933 Fix App Passwords iOS dark mode modal 2023-04-21 22:15:59 -07:00
Paul Frazee 2b96279103 Fix link opening (#512) 2023-04-21 20:28:19 -05:00
Paul Frazee af2fd3cf29 More hotfixes to app passwords (#511)
* Fix app passwords modal on web

* Fix delete app password on web
2023-04-21 19:58:42 -05:00
Paul Frazee 16124b5081 1.20.0 2023-04-21 19:41:59 -05:00
Paul Frazee bbc2ab334d Hotfix feed sizing (#510)
* Fix home feed sizing on web

* Bump api@0.2.8
2023-04-21 19:34:07 -05:00
Ansh 38eb299011 [APP-522] Create & revoke App Passwords within settings (#505)
* create and delete app passwords

* add randomly generated name

* Tweak copy and layout of app passwords

* Improve app passwords on desktop web

* Rearrange settings

* Change app-passwords route and add to backend

* Fix link

* Fix some more desktop web

* Remove log

---------

Co-authored-by: Paul Frazee <pfrazee@gmail.com>
2023-04-21 18:55:29 -05:00
Ollie Hsieh aa56f4a5e2 Move border positioning to FlatList and ScrollView (#509)
* Move border positioning to FlatList and ScrollView

* Fix mobile web tab bar border
2023-04-21 16:40:41 -05:00
Ollie Hsieh f0706dbe9f Add alt text support and rework image layout (#503)
* Add alt text support and rework image layout

* Add additional BottomSheet implementation to account for nested Composer modal

* Use mobile gallery layout on mobile web

* Missing key

* Fix lint

* Move altimage modal into the standard modal system

* Fix overflow wrapping of images

* Fixes to the alt-image modal

* Remove unnecessary switch

* Restore old imagelayoutgrid code

---------

Co-authored-by: Paul Frazee <pfrazee@gmail.com>
2023-04-21 16:20:06 -05:00
Paul Frazee 0f5735b616 Fix profile link 404s on session change & handle change [APP 523] (#507)
* Use DID to link to the user profile to gracefully handle... handle changes

* Reset nav state on active profile change
2023-04-21 12:21:38 -05:00
Paul Frazee 9f9bd314b3 1.19 2023-04-20 17:43:01 -05:00
Paul Frazee e02c926c8a Improvements to notifications screen [APP-520] (#501)
* Refresh or sync notifications when the tab is navigated to

* Fix to bad textnode render

* Speed up initial session load

* Fix lint

* Restore updateSessionState() on session resumption
2023-04-20 17:36:25 -05:00
Paul Frazee f2fe4abdce Fix positioning of the load more button (#502) 2023-04-20 17:34:45 -05:00
Paul Frazee 22884b53ad Thread muting [APP-29] (#500)
* Implement thread muting

* Apply filtering on background fetched notifs

* Implement thread-muting tests
2023-04-20 17:16:56 -05:00
Ollie Hsieh 3e78c71018 Share profile and post (#499) 2023-04-20 13:08:30 -05:00
Ansh 74a1910e12 [APP-514] Fix weird scrollbar position on profile screen (#497)
* add scrollIndicatorInset to ViewSelector FlatList

* Update ViewSelector.tsx

* Update ViewSelector.tsx
2023-04-20 12:32:49 -05:00
Paul Frazee 04e0ebe8fc Feed and notifs improvements (#498)
* Reduce frequency of the notifications sync

* Reduce frequency of home feed polling

* Ensure loading spinner is visible in notifications

* Render notifications loading spinner in the flatlist

* Fixes and performance improvements to notifications

* Render 30+ on notifications if at max

* Fix issue with repeating posts in home feed

* Dont check for unread notifs if we're already at max
2023-04-19 20:11:10 -05:00
Ollie Hsieh b24ba3adc9 Add cursor to clickable elements (#491)
* Add cursor to clickable elements

* Add cursor to clickable elements

* Update per comments

* Fix word wrap in notifications

* Center the web login-load screen

* Add hover states on web

* Fix lint

---------

Co-authored-by: Paul Frazee <pfrazee@gmail.com>
2023-04-19 20:05:10 -05:00
Ansh 1472bd4f17 #420: add updateDataOptimistically utility to disallow like counter out of sync (#446)
* add isLikedPressed flag to disallow like counter out of sync

* create revertible helper for updateDataOptimistically

* test implementation

* Update updateDataOptimistically() and apply to reposts

---------

Co-authored-by: Paul Frazee <pfrazee@gmail.com>
2023-04-19 15:58:24 -05:00
Paul Frazee be83d2933c 1.18 2023-04-19 14:28:49 -05:00
Paul Frazee b40287e4be [APP 513] Label tuning (#496)
* Label updates: break out sexual into 3 categories; tune defaults; improve descriptions

* Fix misapplication of warning in notifications
2023-04-19 14:27:54 -05:00
Ollie Hsieh 3cc0fb1d67 Merge pull request #494 from bluesky-social/ollie/APP-91
[APP-91] Support CMD + Enter to publish post
2023-04-19 12:27:43 -07:00
renahlee 31df05825c Support CMD + Enter to publish post 2023-04-19 11:58:04 -07:00
Paul Frazee 8917cf77a0 Fix: consistently give suggestions in search (#493) 2023-04-19 12:43:34 -05:00
Paul Frazee 192f842ebd Update android version code 2023-04-18 19:00:46 -05:00
Paul Frazee 3e29121f32 1.17 2023-04-18 18:32:47 -05:00
Paul Frazee 75fd653be3 Rework search suggestions for performance (#492) 2023-04-18 18:29:54 -05:00
Paul Frazee 1ab8f31517 Fix user avatar and banner selector on web (#490) 2023-04-18 15:27:42 -05:00
Ansh c8157d6d38 Update README.md (#487)
Update README with new build instructions since we are no longer using the ios and android folders. So, ideally, devs will never need to open XCode and Android Studio (thank the gods)
2023-04-18 13:06:38 -05:00
Ansh 737235f5e0 add edges to ImageViewing component (#471) 2023-04-18 13:06:09 -05:00
Ollie Hsieh c2a4ffc99f Quote post legibility (#486) 2023-04-18 12:53:47 -05:00
Ansh 10621e86e4 APP-70 give profile its own tab mobile (#469)
* add prebuild command to package.json

* add ProfileTab navigator and screen

* add prop to remove back button from profile

* fix MyProfileTabNavigatorParams type

* fix dep array for rendering ProfileHeader

* just added ts-ignore

* enable opening drawer in profile tab

* clean up useNavigationTabState

* clean up code

* fix hideBackButton code flow
2023-04-18 11:19:37 -05:00
Ollie Hsieh 2509290fdd Split image cropping into secondary step (#473)
* Split image cropping into secondary step

* Use ImageModel and GalleryModel

* Add fix for pasting image URLs

* Move models to state folder

* Fix things that broke after rebase

* Latest -- has image display bug

* Remove contentFit

* Fix iOS display in gallery

* Tuneup the api signatures and implement compress/resize on web

* Fix await

* Lint fix and remove unused function

* Fix android image pathing

* Fix external embed x button on android

* Remove min-height from composer (no longer useful and was mispositioning the composer on android)

* Fix e2e picker

---------

Co-authored-by: Paul Frazee <pfrazee@gmail.com>
2023-04-17 15:41:44 -07:00
Paul Frazee 91fadadb58 Fix web home feed sizing and related issues (close #432) (#475)
* Fix web home feed sizing (close #432)

* Fix lint

* Fix positioning of profile not found error

* Fix load latest on mobile

* Fix overflow issues on mobile web (visible in postthread)

* Fix bottom pad on mobile web

* Remove old comment
2023-04-15 10:15:30 -07:00
Paul Frazee a79dcd3d38 Fix: sanitize URLs before placing them on the page (#488) 2023-04-15 09:24:03 -07:00
Paul Frazee a6634ec45d Update bskyweb html styles 2023-04-13 16:36:08 -07:00
Paul Frazee f917c426a0 Web darkmode fixes (#474)
* Change dark mode borders to be slightly lighter than the bg rather than slightly darker

* Add dark mode styling to web composer

* Fix editprofile darkmode
2023-04-13 16:10:27 -07:00
Paul Frazee 5caa6a5e08 Update the bskyweb routes (#468) 2023-04-13 10:12:35 -07:00
Paul Frazee 8aabc70fa9 Update config 2023-04-12 20:49:36 -07:00
Paul Frazee d70182a996 Bump deps 2023-04-12 20:29:33 -07:00
Ansh Nanda c88903dad4 fix composer SafeAreaView 2023-04-12 20:28:01 -07:00
Paul Frazee 0d22767609 1.16 2023-04-12 18:59:35 -07:00
Ansh f2f73e414a temporary android fix for image picker issue (#466) 2023-04-12 18:50:49 -07:00
Ansh ec0510d1d9 add scrollIndicatorInset right: 1 to fix (#464) 2023-04-12 18:50:04 -07:00
Ansh f50f07f562 #435 web dark mode (#455)
* add ThemeProvider to App.web.tsx

* make FlatNavigator use themed color

* fix extra padding on top in web

* add observer to App.web.tsx to make it react to theme changes

* fix TS for useColorSchemeStyle

* add dark mode toggle button to web LeftNav

* fix index.web.tsx border colors for web

* Move the darkmode desktop web toggle to the right nav column

---------

Co-authored-by: Paul Frazee <pfrazee@gmail.com>
2023-04-12 18:49:40 -07:00
Ansh 05e4e4ff93 add maxHeight of 100vh to web modal to stop overflow (#463) 2023-04-12 18:28:45 -07:00
John Fawcett f6769b283f Mobile Web (#427)
* WIP

* WIP

* Fix header offset on web

* Remove debug

* Fix web mobile feed and FAB layout

* Fix modals on mobile web

* Remove dead code

* Remove ios config that shouldnt be committed now

* Move bottom bar into its own folder

* Fix web drawer navigation and state behaviors

* Remove dark mode toggle from web drawer for now

* Fix search on mobile web

* Fix the logged out splash screen on mobile web

* Fixes to detox simulator

---------

Co-authored-by: Paul Frazee <pfrazee@gmail.com>
2023-04-12 18:27:55 -07:00
Paul Frazee 2fed6c4021 Add first round of labeling tools (#467)
* Rework notifications to sync locally in full and give users better control

* Fix positioning of load more btn on web

* Improve behavior of load more notifications btn

* Fix to post rendering

* Fix notification fetch abort condition

* Add start of post-hiding by labels

* Create a standard postcontainer and improve show/hide UI on posts

* Add content hiding to expanded post form

* Improve label rendering to give more context to users when appropriate

* Fix rendering bug

* Add user/profile labeling

* Implement content filtering preferences

* Filter notifications by content prefs

* Update test-pds config

* Bump deps
2023-04-12 18:26:38 -07:00
592 changed files with 63271 additions and 24549 deletions
+3 -3
View File
@@ -14,14 +14,14 @@ module.exports = {
type: 'ios.app',
binaryPath: 'ios/build/Build/Products/Debug-iphonesimulator/bluesky.app',
build:
'xcodebuild -workspace ios/bluesky.xcworkspace -scheme bluesky -configuration Debug -sdk iphonesimulator -derivedDataPath ios/build',
'xcodebuild -workspace ios/Bluesky.xcworkspace -scheme Bluesky -configuration Debug -sdk iphonesimulator -derivedDataPath ios/build',
},
'ios.release': {
type: 'ios.app',
binaryPath:
'ios/build/Build/Products/Release-iphonesimulator/bluesky.app',
build:
'xcodebuild -workspace ios/bluesky.xcworkspace -scheme bluesky -configuration Release -sdk iphonesimulator -derivedDataPath ios/build',
'xcodebuild -workspace ios/Bluesky.xcworkspace -scheme Bluesky -configuration Release -sdk iphonesimulator -derivedDataPath ios/build',
},
'android.debug': {
type: 'android.apk',
@@ -41,7 +41,7 @@ module.exports = {
simulator: {
type: 'ios.simulator',
device: {
type: 'iPhone 14',
type: 'iPhone 15 Pro',
},
},
attached: {
+102
View File
@@ -0,0 +1,102 @@
# OSX
#
.DS_Store
# Xcode
#
build/
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata
*.xccheckout
*.moved-aside
DerivedData
*.hmap
*.ipa
*.xcuserstate
# Android/IntelliJ
#
build/
.idea
.gradle
local.properties
*.iml
*.hprof
# node.js
#
node_modules/
npm-debug.log
yarn-error.log
# BUCK
buck-out/
\.buckd/
*.keystore
!debug.keystore
# fastlane
#
# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
# screenshots whenever they are needed.
# For more information about the recommended setup visit:
# https://docs.fastlane.tools/best-practices/source-control/
*/fastlane/report.xml
*/fastlane/Preview.html
*/fastlane/screenshots
# Bundle artifact
*.jsbundle
# Ruby / CocoaPods
/ios/Pods/
/vendor/bundle/
# Testing
coverage/
junit.xml
artifacts
# Web bundle outputs
/dist/
# Expo
.expo/
dist/
*.jks
*.p8
*.p12
*.key
*.mobileprovision
*.orig.*
web-build/
# Temporary files created by Metro to check the health of the file watcher
.metro-health-check*
# VSCode
.vscode
# gitignore and github actions
!.gitignore
!.github
# Android & iOS folders
android/
ios/
# environment variables
.env
.env.*
# Firebase (Android) Google services
# INCLUDED: google-services.json
+3
View File
@@ -0,0 +1,3 @@
SENTRY_AUTH_TOKEN=
EXPO_PUBLIC_LOG_LEVEL=debug
EXPO_PUBLIC_LOG_DEBUG=
+21 -10
View File
@@ -1,21 +1,32 @@
module.exports = {
root: true,
extends: '@react-native-community',
extends: [
'@react-native-community',
'plugin:react/recommended',
'plugin:react-native-a11y/ios',
'prettier',
],
parser: '@typescript-eslint/parser',
plugins: ['@typescript-eslint', 'detox'],
plugins: ['@typescript-eslint', 'detox', 'react'],
rules: {
'react/no-unescaped-entities': 0,
'react-native/no-inline-styles': 0,
},
ignorePatterns: [
'**/__mocks__/*.ts',
'src/platform/polyfills.ts',
'src/third-party',
'ios',
'android',
'coverage',
'*.lock',
'.husky',
'patches',
'bskyweb',
'*.html',
'bskyweb',
],
overrides: [
{
files: ['*.js', '*.mjs', '*.ts', '*.tsx'],
rules: {
semi: [2, 'never'],
},
},
],
settings: {
componentWrapperFunctions: ['observer'],
},
}
+35
View File
@@ -0,0 +1,35 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug
assignees: ''
---
**Describe the bug**
<!-- A clear and concise description of what the bug is. -->
**To Reproduce**
Steps to reproduce the behavior:
1.
**Expected behavior**
<!-- A clear and concise description of what you expected to happen. -->
**Screenshots**
<!-- If applicable, add screenshots to help explain your problem. -->
**Details**
- Platform: <!-- desktop chrome windows, mobile safari, iOS, Android -->
- Platform version:
- App version:
**Additional context**
<!-- Add any other context about the problem here. -->
+24
View File
@@ -0,0 +1,24 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: feature-request
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
<!-- A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] -->
**Describe the solution you'd like**
<!-- A clear and concise description of what you want to happen. -->
**Describe alternatives you've considered**
<!-- A clear and concise description of any alternative solutions or features you've considered. -->
**Additional context**
<!-- Add any other context or screenshots about the feature request here. -->
@@ -12,6 +12,7 @@ env:
jobs:
bskyweb-container-aws:
if: github.repository == 'bluesky-social/social-app'
runs-on: ubuntu-latest
permissions:
contents: read
@@ -14,6 +14,7 @@ env:
jobs:
bskyweb-container-ghcr:
if: github.repository == 'bluesky-social/social-app'
runs-on: ubuntu-latest
permissions:
contents: read
+2 -2
View File
@@ -19,7 +19,7 @@ jobs:
- name: Set up Go tooling
uses: actions/setup-go@v3
with:
go-version: '1.20'
go-version: '1.21'
- name: Dummy JS File
run: touch bskyweb/static/js/blah.js
- name: Check
@@ -36,7 +36,7 @@ jobs:
- name: Set up Go tooling
uses: actions/setup-go@v3
with:
go-version: '1.20'
go-version: '1.21'
- name: Dummy JS File
run: touch bskyweb/static/js/blah.js
- name: Lint
+5 -3
View File
@@ -17,9 +17,11 @@ jobs:
- name: Check out Git repository
uses: actions/checkout@v3
- name: Yarn install
run: yarn
- name: Typescript & Lint check
run: yarn --frozen-lockfile
- name: Lint check
run: yarn lint
- name: Type check
run: yarn typecheck
testing:
name: Run tests
runs-on: ubuntu-latest
@@ -31,7 +33,7 @@ jobs:
- name: Check out Git repository
uses: actions/checkout@v3
- name: Yarn install
run: yarn
run: yarn --frozen-lockfile
- name: Run tests
run: |
yarn test --forceExit
+14 -1
View File
@@ -82,6 +82,9 @@ web-build/
# Temporary files created by Metro to check the health of the file watcher
.metro-health-check*
# VSCode
.vscode
# gitignore and github actions
!.gitignore
!.github
@@ -89,4 +92,14 @@ web-build/
# Android & iOS folders
android/
ios/
ios/
# environment variables
.env
.env.*
# Firebase (Android) Google services
google-services.json
# Performance results (Flashlight)
.perf/
+4
View File
@@ -0,0 +1,4 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
yarn lint-staged
+4 -6
View File
@@ -1,4 +1,4 @@
FROM golang:1.20-bullseye AS build-env
FROM golang:1.21-bullseye AS build-env
WORKDIR /usr/src/social-app
@@ -13,11 +13,12 @@ ENV GODEBUG="netdns=go"
ENV GOOS="linux"
ENV GOARCH="amd64"
ENV CGO_ENABLED=1
ENV GOEXPERIMENT="loopvar"
COPY . .
#
# Generate the Javascript webpack.
# Generate the JavaScript webpack.
#
RUN mkdir --parents $NVM_DIR && \
wget \
@@ -35,11 +36,8 @@ RUN \. "$NVM_DIR/nvm.sh" && \
# DEBUG
RUN find ./bskyweb/static && find ./web-build/static
# Copy the bundle js files.
RUN cp --verbose ./web-build/static/js/*.* ./bskyweb/static/js/
#
# Generate the bksyweb Go binary.
# Generate the bskyweb Go binary.
#
RUN cd bskyweb/ && \
go mod download && \
+7
View File
@@ -0,0 +1,7 @@
Copyright 2023 Bluesky PBC
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+35
View File
@@ -0,0 +1,35 @@
SHELL = /bin/bash
.SHELLFLAGS = -o pipefail -c
.PHONY: help
help: ## Print info about all commands
@echo "Commands:"
@echo
@grep -E '^[a-zA-Z0-9_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf " \033[01;32m%-20s\033[0m %s\n", $$1, $$2}'
.PHONY: build-web
build-web: ## Compile web bundle, copy to bskyweb directory
yarn build-web
.PHONY: test
test: ## Run all tests
yarn test
.PHONY: lint
lint: ## Run style checks and verify syntax
yarn run lint
#.PHONY: fmt
#fmt: ## Run syntax re-formatting
# yarn prettier
.PHONY: deps
deps: ## Installs dependent libs using 'yarn install'
yarn install --frozen-lockfile
.PHONY: nvm-setup
nvm-setup: ## Use NVM to install and activate node+yarn
nvm install 18
nvm use 18
npm install --global yarn
+66 -42
View File
@@ -1,52 +1,76 @@
# Bluesky
# Bluesky Social App
## Build instructions
Welcome friends! This is the codebase for the Bluesky Social app.
- Setup your environment [using the react native instructions](https://reactnative.dev/docs/environment-setup).
- Setup your environment [for e2e testing using detox](https://wix.github.io/Detox/docs/introduction/getting-started):
- yarn global add detox-cli
- brew tap wix/brew
- brew install applesimutils
- After initial setup:
- `cd ios ; pod install`
- Start the dev servers
- `git clone git@github.com:bluesky-social/atproto.git`
- `cd atproto`
- `yarn`
- `cd packages/dev-env && yarn start`
- Run the dev app
- iOS: `yarn ios`
- Android: `yarn android`
- Web: `yarn web`
- Run e2e tests
- Start in various console tabs:
- `yarn e2e:server`
- `yarn e2e:metro`
- Run once: `yarn e2e:build`
- Each test run: `yarn e2e:run`
- Tips
- `npx react-native info` Checks what has been installed.
- On M1 macs, [you need to exclude "arm64" from the target architectures](https://stackoverflow.com/a/65399525)
- Annoyingly this must be re-set via XCode after every pod install
- The android simulator won't be able to access localhost services unless you run `adb reverse tcp:{PORT} tcp:{PORT}`
- For instance, the localhosted dev-wallet will need `adb reverse tcp:3001 tcp:3001`
- For some reason, the typescript compiler chokes on platform-specific files (e.g. `foo.native.ts`) but only when compiling for Web thus far. Therefore we always have one version of the file which doesn't use a platform specifier, and that should bee the Web version. ([More info](https://stackoverflow.com/questions/44001050/platform-specific-import-component-in-react-native-with-typescript).)
Get the app itself:
## Various notes
- **Web: [bsky.app](https://bsky.app)**
- **iOS: [App Store](https://apps.apple.com/us/app/bluesky-social/id6444370199)**
- **Android: [Play Store](https://play.google.com/store/apps/details?id=xyz.blueskyweb.app&hl=en_US&gl=US)**
### Debugging
## Development Resources
- Note that since 0.70, debugging using the old debugger (which shows up using CMD+D) doesn't work anymore. Follow the instructions below to debug the code: https://reactnative.dev/docs/next/hermes#debugging-js-on-hermes-using-google-chromes-devtools
This is a [React Native](https://reactnative.dev/) application, written in the TypeScript programming language. It builds on the `atproto` TypeScript packages (like [`@atproto/api`](https://www.npmjs.com/package/@atproto/api)), code for which is also on open source, but in [a different git repository](https://github.com/bluesky-social/atproto).
### Running E2E Tests
There is a small amount of Go language source code (in `./bskyweb/`), for a web service that returns the React Native Web application.
- Make sure you've setup your environment following above
- Make sure Metro and the dev server are running
- Run `yarn e2e`
- Find the artifacts in the `artifact` folder
The [Build Instructions](./docs/build.md) are a good place to get started with the app itself.
### Polyfills
The Authenticated Transfer Protocol ("AT Protocol" or "atproto") is a decentralized social media protocol. You don't *need* to understand AT Protocol to work with this application, but it can help. Learn more at:
`./platform/polyfills.*.ts` adds polyfills to the environment. Currently this includes:
- [Overview and Guides](https://atproto.com/guides/overview)
- [Github Discussions](https://github.com/bluesky-social/atproto/discussions) 👈 Great place to ask questions
- [Protocol Specifications](https://atproto.com/specs/atp)
- [Blogpost on self-authenticating data structures](https://blueskyweb.xyz/blog/3-6-2022-a-self-authenticating-social-protocol)
- TextEncoder / TextDecoder
The Bluesky Social application encompasses a set of schemas and APIs built in the overall AT Protocol framework. The namespace for these "Lexicons" is `app.bsky.*`.
## Contributions
> While we do accept contributions, we prioritize high quality issues and pull requests. Adhering to the below guidelines will ensure a more timely review.
**Rules:**
- We may not respond to your issue or PR.
- We may close an issue or PR without much feedback.
- We may lock discussions or contributions if our attention is getting DDOSed.
- We're not going to provide support for build issues.
**Guidelines:**
- Check for existing issues before filing a new one please.
- Open an issue and give some time for discussion before submitting a PR.
- Stay away from PRs like...
- Changing "Post" to "Skeet."
- Refactoring the codebase, eg to replace mobx with redux or something.
- Adding entirely new features without prior discussion.
Remember, we serve a wide community of users. Our day to day involves us constantly asking "which top priority is our top priority." If you submit well-written PRs that solve problems concisely, that's an awesome contribution. Otherwise, as much as we'd love to accept your ideas and contributions, we really don't have the bandwidth. That's what forking is for!
## Forking guidelines
You have our blessing 🪄✨ to fork this application! However, it's very important to be clear to users when you're giving them a fork.
Please be sure to:
- Change all branding in the repository and UI to clearly differentiate from Bluesky.
- Change any support links (feedback, email, terms of service, etc) to your own systems.
- Replace any analytics or error-collection systems with your own so we don't get super confused.
## Security disclosures
If you discover any security issues, please send an email to security@bsky.app. The email is automatically CCed to the entire team and we'll respond promptly.
## Are you a developer interested in building on atproto?
Bluesky is an open social network built on the AT Protocol, a flexible technology that will never lock developers out of the ecosystems that they help build. With atproto, third-party can be as seamless as first-party through custom feeds, federated services, clients, and more.
If you're a developer interested in building on atproto, we'd love to email you a Bluesky invite code. Simply share your GitHub (or similar) profile with us via [this form](https://forms.gle/BF21oxVNZiDjDhXF9).
## License (MIT)
See [./LICENSE](./LICENSE) for the full license.
## P.S.
We ❤️ you and all of the ways you support us. Thank you for making Bluesky a great place!
+77
View File
@@ -0,0 +1,77 @@
# flow.yaml
appId: xyz.blueskyweb.app
---
- launchApp
# Login
# - runFlow:
# when:
# - tapOn: "Sign In"
# - tapOn: "Username or email address"
# - inputText: "ansh.bsky.team"
# - tapOn: "Password"
# - inputText: "PASSWORd"
# - tapOn: "Next"
# Allow notifications if popup is visible
# - runFlow:
# when:
# visible: "Notifications"
# commands:
# - tapOn: "Allow"
# Scroll in main feed
- "scroll"
- "scroll"
- "scroll"
- "scroll"
- "scroll"
- "scroll"
- "scroll"
- "scroll"
# Swipe between feeds
- swipe:
direction: "LEFT"
- swipe:
direction: "LEFT"
- swipe:
direction: "LEFT"
- swipe:
direction: "RIGHT"
- swipe:
direction: "RIGHT"
- swipe:
direction: "RIGHT"
# Go to Notifications
- tapOn:
id: "viewHeaderDrawerBtn"
- tapOn: "Notifications"
- "scroll"
- "scroll"
- "scroll"
- "scroll"
- "scroll"
- swipe:
direction: "DOWN" # Make header visible
# Go to Feeds tab
- tapOn:
id: "viewHeaderDrawerBtn"
- tapOn: "Feeds"
- scrollUntilVisible:
element: "Discover"
direction: UP
- tapOn: "Discover"
- waitForAnimationToEnd
- "scroll"
- "scroll"
- "scroll"
- "scroll"
- "scroll"
# Click on post
- tapOn:
id: "postText"
index: 0
- "scroll"
- "scroll"
- "scroll"
- "scroll"
- "scroll"
+443
View File
@@ -34,6 +34,9 @@ async function main() {
displayName: 'Carla',
description: 'Test user 3',
}))
if (inviteRequired) {
await server.mocker.createInvite(server.mocker.users.alice.did)
}
}
if ('follows' in url.query) {
console.log('Generating mock follows')
@@ -50,6 +53,10 @@ async function main() {
await server.mocker.users[user].agent.post({text: 'Post'})
}
}
if ('feeds' in url.query) {
console.log('Generating mock feed')
await server.mocker.createFeed('alice', 'alice-favs', [])
}
if ('thread' in url.query) {
console.log('Generating mock posts')
const res = await server.mocker.users.bob.agent.post({
@@ -63,6 +70,442 @@ async function main() {
},
})
}
if ('mergefeed' in url.query) {
console.log('Generating mock users')
await server.mocker.createUser('alice')
await server.mocker.createUser('bob')
await server.mocker.createUser('carla')
await server.mocker.createUser('dan')
await server.mocker.users.alice.agent.upsertProfile(() => ({
displayName: 'Alice',
description: 'Test user 1',
}))
await server.mocker.users.bob.agent.upsertProfile(() => ({
displayName: 'Bob',
description: 'Test user 2',
}))
await server.mocker.users.carla.agent.upsertProfile(() => ({
displayName: 'Carla',
description: 'Test user 3',
}))
await server.mocker.users.dan.agent.upsertProfile(() => ({
displayName: 'Dan',
description: 'Test user 4',
}))
console.log('Generating mock follows')
await server.mocker.follow('alice', 'bob')
await server.mocker.follow('alice', 'carla')
console.log('Generating mock posts')
let posts: Record<string, any[]> = {
alice: [],
bob: [],
carla: [],
dan: [],
}
for (let i = 0; i < 10; i++) {
for (let user in server.mocker.users) {
if (user === 'alice') continue
posts[user].push(
await server.mocker.createPost(user, `Post ${i}`),
)
}
}
for (let i = 0; i < 10; i++) {
for (let user in server.mocker.users) {
if (user === 'alice') continue
if (i % 5 === 0) {
await server.mocker.createReply(user, 'Self reply', {
cid: posts[user][i].cid,
uri: posts[user][i].uri,
})
}
if (i % 5 === 1) {
await server.mocker.createReply(user, 'Reply to bob', {
cid: posts.bob[i].cid,
uri: posts.bob[i].uri,
})
}
if (i % 5 === 2) {
await server.mocker.createReply(user, 'Reply to dan', {
cid: posts.dan[i].cid,
uri: posts.dan[i].uri,
})
}
await server.mocker.users[user].agent.post({text: `Post ${i}`})
}
}
console.log('Generating mock feeds')
await server.mocker.createFeed(
'alice',
'alice-favs',
posts.dan.map(p => p.uri),
)
await server.mocker.createFeed(
'alice',
'alice-favs2',
posts.dan.map(p => p.uri),
)
}
if ('labels' in url.query) {
console.log('Generating naughty users with labels')
const anchorPost = await server.mocker.createPost(
'alice',
'Anchor post',
)
for (const user of [
'dmca-account',
'dmca-profile',
'dmca-posts',
'porn-account',
'porn-profile',
'porn-posts',
'nudity-account',
'nudity-profile',
'nudity-posts',
'scam-account',
'scam-profile',
'scam-posts',
'unknown-account',
'unknown-profile',
'unknown-posts',
'hide-account',
'hide-profile',
'hide-posts',
'no-promote-account',
'no-promote-profile',
'no-promote-posts',
'warn-account',
'warn-profile',
'warn-posts',
'muted-account',
'muted-by-list-account',
'blocking-account',
'blockedby-account',
'mutual-block-account',
]) {
await server.mocker.createUser(user)
await server.mocker.follow('alice', user)
await server.mocker.follow(user, 'alice')
await server.mocker.createPost(user, `Unlabeled post from ${user}`)
await server.mocker.createReply(
user,
`Unlabeled reply from ${user}`,
anchorPost,
)
await server.mocker.like(user, anchorPost)
}
await server.mocker.labelAccount('dmca-violation', 'dmca-account')
await server.mocker.labelProfile('dmca-violation', 'dmca-profile')
await server.mocker.labelPost(
'dmca-violation',
await server.mocker.createPost('dmca-posts', 'dmca post'),
)
await server.mocker.labelPost(
'dmca-violation',
await server.mocker.createQuotePost(
'dmca-posts',
'dmca quote post',
anchorPost,
),
)
await server.mocker.labelPost(
'dmca-violation',
await server.mocker.createReply(
'dmca-posts',
'dmca reply',
anchorPost,
),
)
await server.mocker.labelAccount('porn', 'porn-account')
await server.mocker.labelProfile('porn', 'porn-profile')
await server.mocker.labelPost(
'porn',
await server.mocker.createImagePost('porn-posts', 'porn post'),
)
await server.mocker.labelPost(
'porn',
await server.mocker.createQuotePost(
'porn-posts',
'porn quote post',
anchorPost,
),
)
await server.mocker.labelPost(
'porn',
await server.mocker.createReply(
'porn-posts',
'porn reply',
anchorPost,
),
)
await server.mocker.labelAccount('nudity', 'nudity-account')
await server.mocker.labelProfile('nudity', 'nudity-profile')
await server.mocker.labelPost(
'nudity',
await server.mocker.createImagePost('nudity-posts', 'nudity post'),
)
await server.mocker.labelPost(
'nudity',
await server.mocker.createQuotePost(
'nudity-posts',
'nudity quote post',
anchorPost,
),
)
await server.mocker.labelPost(
'nudity',
await server.mocker.createReply(
'nudity-posts',
'nudity reply',
anchorPost,
),
)
await server.mocker.labelAccount('scam', 'scam-account')
await server.mocker.labelProfile('scam', 'scam-profile')
await server.mocker.labelPost(
'scam',
await server.mocker.createPost('scam-posts', 'scam post'),
)
await server.mocker.labelPost(
'scam',
await server.mocker.createQuotePost(
'scam-posts',
'scam quote post',
anchorPost,
),
)
await server.mocker.labelPost(
'scam',
await server.mocker.createReply(
'scam-posts',
'scam reply',
anchorPost,
),
)
await server.mocker.labelAccount(
'not-a-real-label',
'unknown-account',
)
await server.mocker.labelProfile(
'not-a-real-label',
'unknown-profile',
)
await server.mocker.labelPost(
'not-a-real-label',
await server.mocker.createPost('unknown-posts', 'unknown post'),
)
await server.mocker.labelPost(
'not-a-real-label',
await server.mocker.createQuotePost(
'unknown-posts',
'unknown quote post',
anchorPost,
),
)
await server.mocker.labelPost(
'not-a-real-label',
await server.mocker.createReply(
'unknown-posts',
'unknown reply',
anchorPost,
),
)
await server.mocker.labelAccount('!hide', 'hide-account')
await server.mocker.labelProfile('!hide', 'hide-profile')
await server.mocker.labelPost(
'!hide',
await server.mocker.createPost('hide-posts', 'hide post'),
)
await server.mocker.labelPost(
'!hide',
await server.mocker.createQuotePost(
'hide-posts',
'hide quote post',
anchorPost,
),
)
await server.mocker.labelPost(
'!hide',
await server.mocker.createReply(
'hide-posts',
'hide reply',
anchorPost,
),
)
await server.mocker.labelAccount('!no-promote', 'no-promote-account')
await server.mocker.labelProfile('!no-promote', 'no-promote-profile')
await server.mocker.labelPost(
'!no-promote',
await server.mocker.createPost(
'no-promote-posts',
'no-promote post',
),
)
await server.mocker.labelPost(
'!no-promote',
await server.mocker.createQuotePost(
'no-promote-posts',
'no-promote quote post',
anchorPost,
),
)
await server.mocker.labelPost(
'!no-promote',
await server.mocker.createReply(
'no-promote-posts',
'no-promote reply',
anchorPost,
),
)
await server.mocker.labelAccount('!warn', 'warn-account')
await server.mocker.labelProfile('!warn', 'warn-profile')
await server.mocker.labelPost(
'!warn',
await server.mocker.createPost('warn-posts', 'warn post'),
)
await server.mocker.labelPost(
'!warn',
await server.mocker.createQuotePost(
'warn-posts',
'warn quote post',
anchorPost,
),
)
await server.mocker.labelPost(
'!warn',
await server.mocker.createReply(
'warn-posts',
'warn reply',
anchorPost,
),
)
await server.mocker.users.alice.agent.mute('muted-account.test')
await server.mocker.createPost('muted-account', 'muted post')
await server.mocker.createQuotePost(
'muted-account',
'muted quote post',
anchorPost,
)
await server.mocker.createReply(
'muted-account',
'muted reply',
anchorPost,
)
const list = await server.mocker.createMuteList(
'alice',
'Muted Users',
)
await server.mocker.addToMuteList(
'alice',
list,
server.mocker.users['muted-by-list-account'].did,
)
await server.mocker.createPost('muted-by-list-account', 'muted post')
await server.mocker.createQuotePost(
'muted-by-list-account',
'account quote post',
anchorPost,
)
await server.mocker.createReply(
'muted-by-list-account',
'account reply',
anchorPost,
)
await server.mocker.createPost('blocking-account', 'blocking post')
await server.mocker.createQuotePost(
'blocking-account',
'blocking quote post',
anchorPost,
)
await server.mocker.createReply(
'blocking-account',
'blocking reply',
anchorPost,
)
await server.mocker.users.alice.agent.app.bsky.graph.block.create(
{
repo: server.mocker.users.alice.did,
},
{
subject: server.mocker.users['blocking-account'].did,
createdAt: new Date().toISOString(),
},
)
await server.mocker.createPost('blockedby-account', 'blockedby post')
await server.mocker.createQuotePost(
'blockedby-account',
'blockedby quote post',
anchorPost,
)
await server.mocker.createReply(
'blockedby-account',
'blockedby reply',
anchorPost,
)
await server.mocker.users[
'blockedby-account'
].agent.app.bsky.graph.block.create(
{
repo: server.mocker.users['blockedby-account'].did,
},
{
subject: server.mocker.users.alice.did,
createdAt: new Date().toISOString(),
},
)
await server.mocker.createPost(
'mutual-block-account',
'mutual-block post',
)
await server.mocker.createQuotePost(
'mutual-block-account',
'mutual-block quote post',
anchorPost,
)
await server.mocker.createReply(
'mutual-block-account',
'mutual-block reply',
anchorPost,
)
await server.mocker.users.alice.agent.app.bsky.graph.block.create(
{
repo: server.mocker.users.alice.did,
},
{
subject: server.mocker.users['mutual-block-account'].did,
createdAt: new Date().toISOString(),
},
)
await server.mocker.users[
'mutual-block-account'
].agent.app.bsky.graph.block.create(
{
repo: server.mocker.users['mutual-block-account'].did,
},
{
subject: server.mocker.users.alice.did,
createdAt: new Date().toISOString(),
},
)
// flush caches
await server.mocker.testNet.processAll()
}
}
console.log('Ready')
return res.writeHead(200).end(server.pdsUrl)
+3 -4
View File
@@ -1,18 +1,17 @@
/* eslint-env detox/detox */
import {openApp, login, createServer, sleep} from '../util'
import {openApp, loginAsAlice, createServer, sleep} from '../util'
describe('Composer', () => {
let service: string
beforeAll(async () => {
service = await createServer('?users')
await createServer('?users')
await openApp({
permissions: {notifications: 'YES', medialibrary: 'YES', photos: 'YES'},
})
})
it('Login', async () => {
await login(service, 'alice', 'hunter2')
await loginAsAlice()
await element(by.id('homeScreenFeedTabs-Following')).tap()
})
+6 -1
View File
@@ -20,12 +20,17 @@ describe('Create account', () => {
await element(by.id('nextBtn')).tap()
await element(by.id('emailInput')).typeText('example@test.com')
await element(by.id('passwordInput')).typeText('hunter2')
await element(by.id('is13Input')).tap()
await device.takeScreenshot('4- entered account details')
await element(by.id('nextBtn')).tap()
await element(by.id('handleInput')).typeText('e2e-test')
await device.takeScreenshot('4- entered handle')
await element(by.id('nextBtn')).tap()
await expect(element(by.id('welcomeOnboarding'))).toBeVisible()
await element(by.id('continueBtn')).tap()
await expect(element(by.id('recommendedFeedsOnboarding'))).toBeVisible()
await element(by.id('continueBtn')).tap()
await expect(element(by.id('recommendedFollowsOnboarding'))).toBeVisible()
await element(by.id('continueBtn')).tap()
await expect(element(by.id('homeScreen'))).toBeVisible()
})
})
+208
View File
@@ -0,0 +1,208 @@
/* eslint-env detox/detox */
import {openApp, loginAsAlice, loginAsBob, createServer, sleep} from '../util'
describe('Curate lists', () => {
beforeAll(async () => {
await createServer('?users&follows&posts')
await openApp({
permissions: {notifications: 'YES', medialibrary: 'YES', photos: 'YES'},
})
})
it('Login and create a curatelists', async () => {
await expect(element(by.id('signInButton'))).toBeVisible()
await loginAsAlice()
await element(by.id('e2eGotoLists')).tap()
await element(by.id('newUserListBtn')).tap()
await expect(element(by.id('createOrEditListModal'))).toBeVisible()
await element(by.id('editNameInput')).typeText('Good Ppl')
await element(by.id('editDescriptionInput')).typeText('They good')
await element(by.id('saveBtn')).tap()
await expect(element(by.id('createOrEditListModal'))).not.toBeVisible()
await element(by.text('About')).tap()
await expect(element(by.id('headerTitle'))).toHaveText('Good Ppl')
await expect(element(by.id('listDescription'))).toHaveText('They good')
})
it('Edit display name and description via the edit curatelist modal', async () => {
await element(by.id('headerDropdownBtn')).tap()
await element(by.text('Edit List Details')).tap()
await expect(element(by.id('createOrEditListModal'))).toBeVisible()
await element(by.id('editNameInput')).clearText()
await element(by.id('editNameInput')).typeText('Bad Ppl')
await element(by.id('editDescriptionInput')).clearText()
await element(by.id('editDescriptionInput')).typeText('They bad')
await element(by.id('saveBtn')).tap()
await expect(element(by.id('createOrEditListModal'))).not.toBeVisible()
await expect(element(by.id('headerTitle'))).toHaveText('Bad Ppl')
await expect(element(by.id('listDescription'))).toHaveText('They bad')
// have to wait for the toast to clear
await waitFor(element(by.id('headerDropdownBtn')))
.toBeVisible()
.withTimeout(5000)
})
it('Remove description via the edit curatelist modal', async () => {
await element(by.id('headerDropdownBtn')).tap()
await element(by.text('Edit List Details')).tap()
await expect(element(by.id('createOrEditListModal'))).toBeVisible()
await element(by.id('editDescriptionInput')).clearText()
await element(by.id('saveBtn')).tap()
await expect(element(by.id('createOrEditListModal'))).not.toBeVisible()
await expect(element(by.id('listDescription'))).not.toBeVisible()
// have to wait for the toast to clear
await waitFor(element(by.id('headerDropdownBtn')))
.toBeVisible()
.withTimeout(5000)
})
it('Set avi via the edit curatelist modal', async () => {
await expect(element(by.id('userAvatarFallback'))).toExist()
await element(by.id('headerDropdownBtn')).tap()
await element(by.text('Edit List Details')).tap()
await expect(element(by.id('createOrEditListModal'))).toBeVisible()
await element(by.id('changeAvatarBtn')).tap()
await element(by.text('Library')).tap()
await sleep(3e3)
await element(by.id('saveBtn')).tap()
await expect(element(by.id('createOrEditListModal'))).not.toBeVisible()
await expect(element(by.id('userAvatarImage'))).toExist()
// have to wait for the toast to clear
await waitFor(element(by.id('headerDropdownBtn')))
.toBeVisible()
.withTimeout(5000)
})
it('Remove avi via the edit curatelist modal', async () => {
await expect(element(by.id('userAvatarImage'))).toExist()
await element(by.id('headerDropdownBtn')).tap()
await element(by.text('Edit List Details')).tap()
await expect(element(by.id('createOrEditListModal'))).toBeVisible()
await element(by.id('changeAvatarBtn')).tap()
await element(by.text('Remove')).tap()
await element(by.id('saveBtn')).tap()
await expect(element(by.id('createOrEditListModal'))).not.toBeVisible()
await expect(element(by.id('userAvatarFallback'))).toExist()
// have to wait for the toast to clear
await waitFor(element(by.id('headerDropdownBtn')))
.toBeVisible()
.withTimeout(5000)
})
it('Delete the curatelist', async () => {
await element(by.id('headerDropdownBtn')).tap()
await element(by.text('Delete List')).tap()
await element(by.id('confirmBtn')).tap()
await expect(element(by.id('listsEmpty'))).toBeVisible()
})
it('Create a new curatelist', async () => {
await element(by.id('newUserListBtn')).tap()
await expect(element(by.id('createOrEditListModal'))).toBeVisible()
await element(by.id('editNameInput')).typeText('Good Ppl')
await element(by.id('editDescriptionInput')).typeText('They good')
await element(by.id('saveBtn')).tap()
await expect(element(by.id('createOrEditListModal'))).not.toBeVisible()
await element(by.text('About')).tap()
await expect(element(by.id('headerTitle'))).toHaveText('Good Ppl')
await expect(element(by.id('listDescription'))).toHaveText('They good')
})
it('Adds users on curatelists from the list', async () => {
await element(by.text('About')).tap()
await element(by.id('addUserBtn')).tap()
await expect(element(by.id('listAddUserModal'))).toBeVisible()
await waitFor(element(by.id('user-bob.test-addBtn')))
.toBeVisible()
.withTimeout(5000)
await element(by.id('user-bob.test-addBtn')).tap()
await element(by.id('doneBtn')).tap()
await expect(element(by.id('listAddUserModal'))).not.toBeVisible()
await expect(element(by.id('user-bob.test'))).toBeVisible()
})
it('Shows posts by the users in the list', async () => {
await element(by.text('Posts')).tap()
await expect(element(by.id('feedItem-by-bob.test'))).toBeVisible()
})
it('Pins the list', async () => {
await element(by.id('pinBtn')).tap()
await element(by.id('e2eGotoHome')).tap()
await element(by.id('homeScreenFeedTabs-Good Ppl')).tap()
await expect(element(by.id('feedItem-by-bob.test'))).toBeVisible()
await element(by.id('bottomBarFeedsBtn')).tap()
await element(by.id('saved-feed-Good Ppl')).tap()
await expect(element(by.id('feedItem-by-bob.test'))).toBeVisible()
await element(by.id('unpinBtn')).tap()
await element(by.id('bottomBarHomeBtn')).tap()
await expect(
element(by.id('homeScreenFeedTabs-Good Ppl')),
).not.toBeVisible()
await element(by.id('e2eGotoLists')).tap()
await element(by.id('list-Good Ppl')).tap()
})
it('Removes users on curatelists from the list', async () => {
await element(by.text('About')).tap()
await expect(element(by.id('user-bob.test'))).toBeVisible()
await element(by.id('user-bob.test-editBtn')).tap()
await expect(element(by.id('userAddRemoveListsModal'))).toBeVisible()
await element(by.id('toggleBtn-Good Ppl')).tap()
await element(by.id('saveBtn')).tap()
await expect(element(by.id('userAddRemoveListsModal'))).not.toBeVisible()
})
it('Shows the curatelist on my profile', async () => {
await element(by.id('bottomBarProfileBtn')).tap()
await element(by.id('selector')).swipe('left')
await element(by.id('selector-4')).tap()
await element(by.id('list-Good Ppl')).tap()
})
it('Adds and removes users on curatelists from the profile', async () => {
await element(by.id('bottomBarSearchBtn')).tap()
await element(by.id('searchTextInput')).typeText('bob')
await element(by.id('searchAutoCompleteResult-bob.test')).tap()
await expect(element(by.id('profileView'))).toBeVisible()
await element(by.id('profileHeaderDropdownBtn')).tap()
await element(by.text('Add to Lists')).tap()
await expect(element(by.id('userAddRemoveListsModal'))).toBeVisible()
await element(by.id('toggleBtn-Good Ppl')).tap()
await element(by.id('saveBtn')).tap()
await expect(element(by.id('userAddRemoveListsModal'))).not.toBeVisible()
await element(by.id('profileHeaderDropdownBtn')).tap()
await element(by.text('Add to Lists')).tap()
await expect(element(by.id('userAddRemoveListsModal'))).toBeVisible()
await element(by.id('toggleBtn-Good Ppl')).tap()
await element(by.id('saveBtn')).tap()
await expect(element(by.id('userAddRemoveListsModal'))).not.toBeVisible()
})
it('Can report a user list', async () => {
await element(by.id('e2eGotoSettings')).tap()
await element(by.id('signOutBtn')).tap()
await loginAsBob()
await element(by.id('bottomBarSearchBtn')).tap()
await element(by.id('searchTextInput')).typeText('alice')
await element(by.id('searchAutoCompleteResult-alice.test')).tap()
await element(by.id('selector')).swipe('left')
await element(by.id('selector-3')).tap()
await element(by.id('list-Good Ppl')).tap()
await element(by.id('headerDropdownBtn')).tap()
await element(by.text('Report List')).tap()
await expect(element(by.id('reportModal'))).toBeVisible()
await expect(element(by.text('Report List'))).toBeVisible()
await element(
by.id('reportReasonRadios-com.atproto.moderation.defs#reasonRude'),
).tap()
await element(by.id('sendReportBtn')).tap()
await expect(element(by.id('reportModal'))).not.toBeVisible()
})
})
+10 -9
View File
@@ -1,16 +1,15 @@
/* eslint-env detox/detox */
import {openApp, login, createServer} from '../util'
import {openApp, loginAsAlice, createServer} from '../util'
describe('Home screen', () => {
let service: string
beforeAll(async () => {
service = await createServer('?users&follows&posts')
await createServer('?users&follows&posts')
await openApp({permissions: {notifications: 'YES'}})
})
it('Login', async () => {
await login(service, 'alice', 'hunter2')
await loginAsAlice()
await element(by.id('homeScreenFeedTabs-Following')).tap()
})
@@ -55,11 +54,13 @@ describe('Home screen', () => {
await element(by.id('postDropdownBtn').withAncestor(carlaPosts))
.atIndex(0)
.tap()
await element(by.id('postDropdownReportBtn')).tap()
await expect(element(by.id('reportPostModal'))).toBeVisible()
await element(by.id('reportPostRadios-spam')).tap()
await element(by.text('Report post')).tap()
await expect(element(by.id('reportModal'))).toBeVisible()
await element(
by.id('reportReasonRadios-com.atproto.moderation.defs#reasonSpam'),
).tap()
await element(by.id('sendReportBtn')).tap()
await expect(element(by.id('reportPostModal'))).not.toBeVisible()
await expect(element(by.id('reportModal'))).not.toBeVisible()
})
it('Can swipe between feeds', async () => {
@@ -82,7 +83,7 @@ describe('Home screen', () => {
await element(by.id('postDropdownBtn').withAncestor(alicePosts))
.atIndex(0)
.tap()
await element(by.id('postDropdownDeleteBtn')).tap()
await element(by.text('Delete post')).tap()
await expect(element(by.id('confirmModal'))).toBeVisible()
await element(by.id('confirmBtn')).tap()
await expect(
+21 -16
View File
@@ -1,6 +1,11 @@
/* eslint-env detox/detox */
import {openApp, login, createServer} from '../util'
/**
* This test is being skipped until we can resolve the detox crash issue
* with the side drawer.
*/
import {openApp, loginAsAlice, createServer} from '../util'
describe('invite-codes', () => {
let service: string
@@ -12,17 +17,13 @@ describe('invite-codes', () => {
it('I can fetch invite codes', async () => {
await expect(element(by.id('signInButton'))).toBeVisible()
await login(service, 'alice', 'hunter2')
await element(by.id('viewHeaderDrawerBtn')).tap()
await expect(element(by.id('drawer'))).toBeVisible()
await element(by.id('menuItemInviteCodes')).tap()
await loginAsAlice()
await element(by.id('e2eOpenInviteCodesModal')).tap()
await expect(element(by.id('inviteCodesModal'))).toBeVisible()
const attrs = await element(by.id('inviteCode-0-code')).getAttributes()
inviteCode = attrs.text
await element(by.id('closeBtn')).tap()
await element(by.id('viewHeaderDrawerBtn')).tap()
await element(by.id('menuItemButton-Settings')).tap()
await element(by.id('signOutBtn')).tap()
await element(by.id('e2eSignOut')).tap()
})
it('I can create a new account with the invite code', async () => {
@@ -37,23 +38,27 @@ describe('invite-codes', () => {
await element(by.id('inviteCodeInput')).typeText(inviteCode)
await element(by.id('emailInput')).typeText('example@test.com')
await element(by.id('passwordInput')).typeText('hunter2')
await element(by.id('is13Input')).tap()
await device.takeScreenshot('4- entered account details')
await element(by.id('nextBtn')).tap()
await element(by.id('handleInput')).typeText('e2e-test')
await device.takeScreenshot('4- entered handle')
await element(by.id('nextBtn')).tap()
await expect(element(by.id('welcomeOnboarding'))).toBeVisible()
await element(by.id('continueBtn')).tap()
await expect(element(by.id('recommendedFeedsOnboarding'))).toBeVisible()
await element(by.id('continueBtn')).tap()
await expect(element(by.id('recommendedFollowsOnboarding'))).toBeVisible()
await element(by.id('continueBtn')).tap()
await expect(element(by.id('homeScreen'))).toBeVisible()
await element(by.id('viewHeaderDrawerBtn')).tap()
await element(by.id('menuItemButton-Settings')).tap()
await element(by.id('signOutBtn')).tap()
})
it('I get a notification for the new user', async () => {
await expect(element(by.id('signInButton'))).toBeVisible()
await login(service, 'alice', 'hunter2')
await element(by.id('viewHeaderDrawerBtn')).tap()
await element(by.id('menuItemButton-Notifications')).tap()
await element(by.id('e2eSignOut')).tap()
await loginAsAlice()
await waitFor(element(by.id('homeScreen')))
.toBeVisible()
.withTimeout(5000)
await element(by.id('bottomBarNotificationsBtn')).tap()
await expect(element(by.id('invitedUser'))).toBeVisible()
})
+157
View File
@@ -0,0 +1,157 @@
/* eslint-env detox/detox */
import {openApp, loginAsAlice, createServer} from '../util'
describe('Mergefeed', () => {
beforeAll(async () => {
await createServer('?mergefeed')
await openApp({permissions: {notifications: 'YES'}})
})
it('Login', async () => {
await loginAsAlice()
await element(by.id('e2eToggleMergefeed')).tap()
})
it('Sees the expected mix of posts with default filters', async () => {
await element(by.id('followingFeedPage-feed-flatlist')).swipe(
'down',
'slow',
1,
0.5,
0.5,
)
// followed users
await expect(
element(
by.id('postText').withAncestor(by.id('feedItem-by-carla.test')),
).atIndex(0),
).toHaveText('Post 9')
await expect(
element(
by.id('postText').withAncestor(by.id('feedItem-by-bob.test')),
).atIndex(0),
).toHaveText('Post 9')
await element(by.id('followingFeedPage-feed-flatlist')).swipe(
'up',
'fast',
1,
0.5,
0.5,
)
// feed users
await expect(
element(
by.id('postText').withAncestor(by.id('feedItem-by-dan.test')),
).atIndex(0),
).toHaveText('Post 0')
})
it('Sees the expected mix of posts with replies disabled', async () => {
await element(by.id('followingFeedPage-feed-flatlist')).swipe(
'down',
'fast',
1,
0.5,
0.5,
)
await element(by.id('followingFeedPage-feed-flatlist')).swipe(
'down',
'fast',
1,
0.5,
0.5,
)
await element(by.id('viewHeaderHomeFeedPrefsBtn')).tap()
await element(by.id('toggleRepliesBtn')).tap()
await element(by.id('confirmBtn')).tap()
await element(by.id('followingFeedPage-feed-flatlist')).swipe(
'down',
'slow',
1,
0.5,
0.5,
)
// followed users
await expect(
element(
by.id('postText').withAncestor(by.id('feedItem-by-carla.test')),
).atIndex(0),
).toHaveText('Post 9')
await expect(
element(
by.id('postText').withAncestor(by.id('feedItem-by-bob.test')),
).atIndex(0),
).toHaveText('Post 9')
await element(by.id('followingFeedPage-feed-flatlist')).swipe(
'up',
'fast',
1,
0.5,
0.5,
)
// feed users
await expect(
element(
by.id('postText').withAncestor(by.id('feedItem-by-dan.test')),
).atIndex(0),
).toHaveText('Post 0')
})
it('Sees the expected mix of posts with no follows', async () => {
await element(by.id('followingFeedPage-feed-flatlist')).swipe(
'down',
'fast',
1,
0.5,
0.5,
)
await element(by.id('bottomBarSearchBtn')).tap()
await element(by.id('searchTextInput')).typeText('bob')
await element(by.id('searchAutoCompleteResult-bob.test')).tap()
await expect(element(by.id('profileView'))).toBeVisible()
await element(by.id('unfollowBtn')).tap()
await element(by.id('profileHeaderBackBtn')).tap()
// have to wait for the toast to clear
await waitFor(element(by.id('searchTextInputClearBtn')))
.toBeVisible()
.withTimeout(5000)
await element(by.id('searchTextInputClearBtn')).tap()
await element(by.id('searchTextInput')).typeText('carla')
await element(by.id('searchAutoCompleteResult-carla.test')).tap()
await expect(element(by.id('profileView'))).toBeVisible()
await element(by.id('unfollowBtn')).tap()
await element(by.id('profileHeaderBackBtn')).tap()
await element(by.id('bottomBarHomeBtn')).tap()
await element(by.id('followingFeedPage-feed-flatlist')).swipe(
'down',
'slow',
1,
0.5,
0.5,
)
await element(by.id('followingFeedPage-feed-flatlist')).swipe(
'down',
'slow',
1,
0.5,
0.5,
)
// followed users NOT present
await expect(element(by.id('feedItem-by-carla.test'))).not.toExist()
await expect(element(by.id('feedItem-by-bob.test'))).not.toExist()
// feed users
await expect(
element(
by.id('postText').withAncestor(by.id('feedItem-by-dan.test')),
).atIndex(0),
).toHaveText('Post 0')
})
})
+187
View File
@@ -0,0 +1,187 @@
/* eslint-env detox/detox */
import {openApp, loginAsAlice, loginAsBob, createServer, sleep} from '../util'
describe('Mod lists', () => {
beforeAll(async () => {
await createServer('?users&follows&labels')
await openApp({
permissions: {notifications: 'YES', medialibrary: 'YES', photos: 'YES'},
})
})
it('Login and view my modlists', async () => {
await expect(element(by.id('signInButton'))).toBeVisible()
await loginAsAlice()
await element(by.id('e2eGotoModeration')).tap()
await element(by.id('moderationlistsBtn')).tap()
await expect(element(by.id('list-Muted Users'))).toBeVisible()
await element(by.id('list-Muted Users')).tap()
await expect(
element(by.id('user-muted-by-list-account.test')),
).toBeVisible()
})
it('Toggle mute subscription', async () => {
await element(by.id('unmuteBtn')).tap()
await element(by.id('subscribeBtn')).tap()
await element(by.text('Mute accounts')).tap()
await element(by.id('confirmBtn')).tap()
})
it('Edit display name and description via the edit modlist modal', async () => {
await element(by.id('headerDropdownBtn')).tap()
await element(by.text('Edit List Details')).tap()
await expect(element(by.id('createOrEditListModal'))).toBeVisible()
await element(by.id('editNameInput')).clearText()
await element(by.id('editNameInput')).typeText('Bad Ppl')
await element(by.id('editDescriptionInput')).clearText()
await element(by.id('editDescriptionInput')).typeText('They bad')
await element(by.id('saveBtn')).tap()
await expect(element(by.id('createOrEditListModal'))).not.toBeVisible()
await expect(element(by.id('headerTitle'))).toHaveText('Bad Ppl')
await expect(element(by.id('listDescription'))).toHaveText('They bad')
// have to wait for the toast to clear
await waitFor(element(by.id('headerDropdownBtn')))
.toBeVisible()
.withTimeout(5000)
})
it('Remove description via the edit modlist modal', async () => {
await element(by.id('headerDropdownBtn')).tap()
await element(by.text('Edit List Details')).tap()
await expect(element(by.id('createOrEditListModal'))).toBeVisible()
await element(by.id('editDescriptionInput')).clearText()
await element(by.id('saveBtn')).tap()
await expect(element(by.id('createOrEditListModal'))).not.toBeVisible()
await expect(element(by.id('listDescription'))).not.toBeVisible()
// have to wait for the toast to clear
await waitFor(element(by.id('headerDropdownBtn')))
.toBeVisible()
.withTimeout(5000)
})
it('Set avi via the edit modlist modal', async () => {
await expect(element(by.id('userAvatarFallback'))).toExist()
await element(by.id('headerDropdownBtn')).tap()
await element(by.text('Edit List Details')).tap()
await expect(element(by.id('createOrEditListModal'))).toBeVisible()
await element(by.id('changeAvatarBtn')).tap()
await element(by.text('Library')).tap()
await sleep(3e3)
await element(by.id('saveBtn')).tap()
await expect(element(by.id('createOrEditListModal'))).not.toBeVisible()
await expect(element(by.id('userAvatarImage'))).toExist()
// have to wait for the toast to clear
await waitFor(element(by.id('headerDropdownBtn')))
.toBeVisible()
.withTimeout(5000)
})
it('Remove avi via the edit modlist modal', async () => {
await expect(element(by.id('userAvatarImage'))).toExist()
await element(by.id('headerDropdownBtn')).tap()
await element(by.text('Edit List Details')).tap()
await expect(element(by.id('createOrEditListModal'))).toBeVisible()
await element(by.id('changeAvatarBtn')).tap()
await element(by.text('Remove')).tap()
await element(by.id('saveBtn')).tap()
await expect(element(by.id('createOrEditListModal'))).not.toBeVisible()
await expect(element(by.id('userAvatarFallback'))).toExist()
// have to wait for the toast to clear
await waitFor(element(by.id('headerDropdownBtn')))
.toBeVisible()
.withTimeout(5000)
})
it('Delete the modlist', async () => {
await element(by.id('headerDropdownBtn')).tap()
await element(by.text('Delete List')).tap()
await element(by.id('confirmBtn')).tap()
await expect(element(by.id('listsEmpty'))).toBeVisible()
})
it('Create a new modlist', async () => {
await element(by.id('newModListBtn')).tap()
await expect(element(by.id('createOrEditListModal'))).toBeVisible()
await element(by.id('editNameInput')).typeText('Bad Ppl')
await element(by.id('editDescriptionInput')).typeText('They bad')
await element(by.id('saveBtn')).tap()
await expect(element(by.id('createOrEditListModal'))).not.toBeVisible()
await expect(element(by.id('headerTitle'))).toHaveText('Bad Ppl')
await expect(element(by.id('listDescription'))).toHaveText('They bad')
})
it('Adds and removes users on modlists from the list', async () => {
await element(by.id('addUserBtn')).tap()
await expect(element(by.id('listAddUserModal'))).toBeVisible()
await waitFor(element(by.id('user-warn-posts.test-addBtn')))
.toBeVisible()
.withTimeout(5000)
await element(by.id('user-warn-posts.test-addBtn')).tap()
await element(by.id('doneBtn')).tap()
await expect(element(by.id('listAddUserModal'))).not.toBeVisible()
await element(by.id('listItems-flatlist')).swipe(
'down',
'slow',
1,
0.5,
0.5,
)
await expect(element(by.id('user-warn-posts.test'))).toBeVisible()
await element(by.id('user-warn-posts.test-editBtn')).tap()
await expect(element(by.id('userAddRemoveListsModal'))).toBeVisible()
await element(by.id('toggleBtn-Bad Ppl')).tap()
await element(by.id('saveBtn')).tap()
await expect(element(by.id('userAddRemoveListsModal'))).not.toBeVisible()
})
it('Shows the modlist on my profile', async () => {
await element(by.id('bottomBarProfileBtn')).tap()
await element(by.id('selector')).swipe('left')
await element(by.id('selector-4')).tap()
await element(by.id('list-Bad Ppl')).tap()
})
it('Adds and removes users on modlists from the profile', async () => {
await element(by.id('bottomBarSearchBtn')).tap()
await element(by.id('searchTextInput')).typeText('bob')
await element(by.id('searchAutoCompleteResult-bob.test')).tap()
await expect(element(by.id('profileView'))).toBeVisible()
await element(by.id('profileHeaderDropdownBtn')).tap()
await element(by.text('Add to Lists')).tap()
await expect(element(by.id('userAddRemoveListsModal'))).toBeVisible()
await element(by.id('toggleBtn-Bad Ppl')).tap()
await element(by.id('saveBtn')).tap()
await expect(element(by.id('userAddRemoveListsModal'))).not.toBeVisible()
await element(by.id('profileHeaderDropdownBtn')).tap()
await element(by.text('Add to Lists')).tap()
await expect(element(by.id('userAddRemoveListsModal'))).toBeVisible()
await element(by.id('toggleBtn-Bad Ppl')).tap()
await element(by.id('saveBtn')).tap()
await expect(element(by.id('userAddRemoveListsModal'))).not.toBeVisible()
})
it('Can report a mute list', async () => {
await element(by.id('e2eGotoSettings')).tap()
await element(by.id('signOutBtn')).tap()
await loginAsBob()
await element(by.id('bottomBarSearchBtn')).tap()
await element(by.id('searchTextInput')).typeText('alice')
await element(by.id('searchAutoCompleteResult-alice.test')).tap()
await element(by.id('selector')).swipe('left')
await element(by.id('selector-3')).tap()
await element(by.id('list-Bad Ppl')).tap()
await element(by.id('headerDropdownBtn')).tap()
await element(by.text('Report List')).tap()
await expect(element(by.id('reportModal'))).toBeVisible()
await expect(element(by.text('Report List'))).toBeVisible()
await element(
by.id('reportReasonRadios-com.atproto.moderation.defs#reasonRude'),
).tap()
await element(by.id('sendReportBtn')).tap()
await expect(element(by.id('reportModal'))).not.toBeVisible()
})
})
+34 -23
View File
@@ -1,11 +1,10 @@
/* eslint-env detox/detox */
import {openApp, login, createServer, sleep} from '../util'
import {openApp, loginAsAlice, createServer, sleep} from '../util'
describe('Profile screen', () => {
let service: string
beforeAll(async () => {
service = await createServer('?users&posts')
await createServer('?users&posts&feeds')
await openApp({
permissions: {notifications: 'YES', medialibrary: 'YES', photos: 'YES'},
})
@@ -13,10 +12,18 @@ describe('Profile screen', () => {
it('Login and navigate to my profile', async () => {
await expect(element(by.id('signInButton'))).toBeVisible()
await login(service, 'alice', 'hunter2')
await loginAsAlice()
await element(by.id('bottomBarProfileBtn')).tap()
})
it('Can see feeds', async () => {
await element(by.id('selector')).swipe('left')
await element(by.id('selector-4')).tap()
await expect(element(by.id('feed-alice-favs'))).toBeVisible()
await element(by.id('selector')).swipe('right')
await element(by.id('selector-0')).tap()
})
it('Open and close edit profile modal', async () => {
await element(by.id('profileHeaderEditProfileButton')).tap()
await expect(element(by.id('editProfileModal'))).toBeVisible()
@@ -53,7 +60,7 @@ describe('Profile screen', () => {
await expect(element(by.id('profileHeaderDisplayName'))).toHaveText(
'alice.test',
)
await expect(element(by.id('profileHeaderDescription'))).toHaveText('')
await expect(element(by.id('profileHeaderDescription'))).not.toExist()
})
it('Set avi and banner via the edit profile modal', async () => {
@@ -62,10 +69,10 @@ describe('Profile screen', () => {
await element(by.id('profileHeaderEditProfileButton')).tap()
await expect(element(by.id('editProfileModal'))).toBeVisible()
await element(by.id('changeBannerBtn')).tap()
await element(by.id('changeBannerLibraryBtn')).tap()
await element(by.text('Library')).tap()
await sleep(3e3)
await element(by.id('changeAvatarBtn')).tap()
await element(by.id('changeAvatarLibraryBtn')).tap()
await element(by.text('Library')).tap()
await sleep(3e3)
await element(by.id('editProfileSaveBtn')).tap()
await expect(element(by.id('editProfileModal'))).not.toBeVisible()
@@ -79,9 +86,9 @@ describe('Profile screen', () => {
await element(by.id('profileHeaderEditProfileButton')).tap()
await expect(element(by.id('editProfileModal'))).toBeVisible()
await element(by.id('changeBannerBtn')).tap()
await element(by.id('changeBannerRemoveBtn')).tap()
await element(by.text('Remove')).tap()
await element(by.id('changeAvatarBtn')).tap()
await element(by.id('changeAvatarRemoveBtn')).tap()
await element(by.text('Remove')).tap()
await element(by.id('editProfileSaveBtn')).tap()
await expect(element(by.id('editProfileModal'))).not.toBeVisible()
await expect(element(by.id('userBannerFallback'))).toExist()
@@ -93,7 +100,7 @@ describe('Profile screen', () => {
// have to wait for the toast to clear
await waitFor(element(by.id('searchTextInput')))
.toBeVisible()
.withTimeout(2000)
.withTimeout(5000)
await element(by.id('searchTextInput')).typeText('bob')
await element(by.id('searchAutoCompleteResult-bob.test')).tap()
await expect(element(by.id('profileView'))).toBeVisible()
@@ -107,22 +114,24 @@ describe('Profile screen', () => {
})
it('Can mute/unmute another user', async () => {
await expect(element(by.id('profileHeaderMutedNotice'))).not.toExist()
await expect(element(by.id('profileHeaderAlert'))).not.toExist()
await element(by.id('profileHeaderDropdownBtn')).tap()
await element(by.id('profileHeaderDropdownMuteBtn')).tap()
await expect(element(by.id('profileHeaderMutedNotice'))).toBeVisible()
await element(by.text('Mute Account')).tap()
await expect(element(by.id('profileHeaderAlert'))).toBeVisible()
await element(by.id('profileHeaderDropdownBtn')).tap()
await element(by.id('profileHeaderDropdownMuteBtn')).tap()
await expect(element(by.id('profileHeaderMutedNotice'))).not.toExist()
await element(by.text('Unmute Account')).tap()
await expect(element(by.id('profileHeaderAlert'))).not.toExist()
})
it('Can report another user', async () => {
await element(by.id('profileHeaderDropdownBtn')).tap()
await element(by.id('profileHeaderDropdownReportBtn')).tap()
await expect(element(by.id('reportAccountModal'))).toBeVisible()
await element(by.id('reportAccountRadios-spam')).tap()
await element(by.text('Report Account')).tap()
await expect(element(by.id('reportModal'))).toBeVisible()
await element(
by.id('reportReasonRadios-com.atproto.moderation.defs#reasonSpam'),
).tap()
await element(by.id('sendReportBtn')).tap()
await expect(element(by.id('reportAccountModal'))).not.toBeVisible()
await expect(element(by.id('reportModal'))).not.toBeVisible()
})
it('Can like posts', async () => {
@@ -164,10 +173,12 @@ describe('Profile screen', () => {
it('Can report posts', async () => {
const posts = by.id('feedItem-by-bob.test')
await element(by.id('postDropdownBtn').withAncestor(posts)).atIndex(0).tap()
await element(by.id('postDropdownReportBtn')).tap()
await expect(element(by.id('reportPostModal'))).toBeVisible()
await element(by.id('reportPostRadios-spam')).tap()
await element(by.text('Report post')).tap()
await expect(element(by.id('reportModal'))).toBeVisible()
await element(
by.id('reportReasonRadios-com.atproto.moderation.defs#reasonSpam'),
).tap()
await element(by.id('sendReportBtn')).tap()
await expect(element(by.id('reportPostModal'))).not.toBeVisible()
await expect(element(by.id('reportModal'))).not.toBeVisible()
})
})
+3 -4
View File
@@ -1,18 +1,17 @@
/* eslint-env detox/detox */
import {openApp, login, createServer} from '../util'
import {openApp, loginAsAlice, createServer} from '../util'
describe('Search screen', () => {
let service: string
beforeAll(async () => {
service = await createServer('?users')
await createServer('?users')
await openApp({
permissions: {notifications: 'YES', medialibrary: 'YES', photos: 'YES'},
})
})
it('Login', async () => {
await login(service, 'alice', 'hunter2')
await loginAsAlice()
})
it('Navigate to another user profile via autocomplete', async () => {
+33
View File
@@ -0,0 +1,33 @@
/* eslint-env detox/detox */
import {openApp, loginAsAlice, createServer, sleep} from '../util'
describe('Self-labeling', () => {
beforeAll(async () => {
await createServer('?users')
await openApp({
permissions: {notifications: 'YES', medialibrary: 'YES', photos: 'YES'},
})
})
it('Login', async () => {
await loginAsAlice()
await element(by.id('homeScreenFeedTabs-Following')).tap()
})
it('Post an image with the porn label', async () => {
await element(by.id('composeFAB')).tap()
await element(by.id('composerTextInput')).typeText('Post with an image')
await element(by.id('openGalleryBtn')).tap()
await sleep(3e3)
await element(by.id('labelsBtn')).tap()
await element(by.id('pornLabelBtn')).tap()
await element(by.id('confirmBtn')).tap()
await element(by.id('composerPublishBtn')).tap()
await expect(element(by.id('composeFAB'))).toBeVisible()
const posts = by.id('feedItem-by-alice.test')
await expect(
element(by.id('contentHider-embed').withAncestor(posts)).atIndex(0),
).toExist()
})
})
@@ -1,16 +1,15 @@
/* eslint-env detox/detox */
import {openApp, login, createServer} from '../util'
import {openApp, loginAsAlice, createServer} from '../util'
describe('Shell', () => {
let service: string
beforeAll(async () => {
service = await createServer('?users')
await createServer('?users')
await openApp({permissions: {notifications: 'YES'}})
})
it('Login', async () => {
await login(service, 'alice', 'hunter2')
await loginAsAlice()
await element(by.id('homeScreenFeedTabs-Following')).tap()
})
+101
View File
@@ -0,0 +1,101 @@
/* eslint-env detox/detox */
import {openApp, loginAsAlice, loginAsBob, createServer} from '../util'
describe('Thread muting', () => {
beforeAll(async () => {
await createServer('?users&follows')
await openApp({permissions: {notifications: 'YES'}})
})
it('Login, create a thread, and log out', async () => {
await loginAsAlice()
await element(by.id('homeScreenFeedTabs-Following')).tap()
await element(by.id('composeFAB')).tap()
await element(by.id('composerTextInput')).typeText('Test thread')
await element(by.id('composerPublishBtn')).tap()
await expect(element(by.id('composeFAB'))).toBeVisible()
})
it('Login, reply to the thread, and log out', async () => {
await loginAsBob()
await element(by.id('homeScreenFeedTabs-Following')).tap()
const alicePosts = by.id('feedItem-by-alice.test')
await element(by.id('replyBtn').withAncestor(alicePosts)).atIndex(0).tap()
await element(by.id('composerTextInput')).typeText('Reply 1')
await element(by.id('composerPublishBtn')).tap()
await expect(element(by.id('composeFAB'))).toBeVisible()
})
it('Login, confirm notification exists, mute thread, and log out', async () => {
await loginAsAlice()
await element(by.id('bottomBarNotificationsBtn')).tap()
const bobNotifs = by.id('feedItem-by-bob.test')
await expect(
element(by.id('postText').withAncestor(bobNotifs)).atIndex(0),
).toHaveText('Reply 1')
await element(by.id('postDropdownBtn').withAncestor(bobNotifs))
.atIndex(0)
.tap()
await element(by.text('Mute thread')).tap()
// have to wait for the toast to clear
await waitFor(element(by.id('viewHeaderDrawerBtn')))
.toBeVisible()
.withTimeout(5000)
})
it('Login, reply to the thread twice, and log out', async () => {
await loginAsBob()
await element(by.id('bottomBarProfileBtn')).tap()
await element(by.id('selector-1')).tap()
const bobPosts = by.id('feedItem-by-bob.test')
await element(by.id('replyBtn').withAncestor(bobPosts)).atIndex(0).tap()
await element(by.id('composerTextInput')).typeText('Reply 2')
await element(by.id('composerPublishBtn')).tap()
await expect(element(by.id('composeFAB'))).toBeVisible()
const alicePosts = by.id('feedItem-by-alice.test')
await element(by.id('replyBtn').withAncestor(alicePosts)).atIndex(0).tap()
await element(by.id('composerTextInput')).typeText('Reply 3')
await element(by.id('composerPublishBtn')).tap()
await expect(element(by.id('composeFAB'))).toBeVisible()
await element(by.id('bottomBarHomeBtn')).tap()
})
it('Login, confirm notifications dont exist, unmute the thread, confirm notifications exist', async () => {
await loginAsAlice()
await element(by.id('bottomBarNotificationsBtn')).tap()
const bobNotifs = by.id('feedItem-by-bob.test')
await expect(
element(by.id('postText').withAncestor(bobNotifs)).atIndex(0),
).not.toExist()
await element(by.id('bottomBarHomeBtn')).tap()
const alicePosts = by.id('feedItem-by-alice.test')
await element(by.id('postDropdownBtn').withAncestor(alicePosts))
.atIndex(0)
.tap()
await element(by.text('Unmute thread')).tap()
// TODO
// the swipe down to trigger PTR isnt working and I dont want to block on this
// -prf
// await element(by.id('bottomBarNotificationsBtn')).tap()
// await element(by.id('notifsFeed')).swipe('down', 'fast')
// await waitFor(element(by.id('postText').withAncestor(bobNotifs)))
// .toBeVisible()
// .withTimeout(5000)
// await expect(
// element(by.id('postText').withAncestor(bobNotifs)).atIndex(0),
// ).toHaveText('Reply 2')
// await expect(
// element(by.id('postText').withAncestor(bobNotifs)).atIndex(1),
// ).toHaveText('Reply 3')
// await expect(
// element(by.id('postText').withAncestor(bobNotifs)).atIndex(2),
// ).toHaveText('Reply 1')
})
})
+15 -12
View File
@@ -1,16 +1,15 @@
/* eslint-env detox/detox */
import {openApp, login, createServer} from '../util'
import {openApp, loginAsAlice, createServer} from '../util'
describe('Thread screen', () => {
let service: string
beforeAll(async () => {
service = await createServer('?users&follows&thread')
await createServer('?users&follows&thread')
await openApp({permissions: {notifications: 'YES'}})
})
it('Login & navigate to thread', async () => {
await login(service, 'alice', 'hunter2')
await loginAsAlice()
await element(by.id('homeScreenFeedTabs-Following')).tap()
await element(by.id('feedItem-by-bob.test')).atIndex(0).tap()
await expect(
@@ -104,20 +103,24 @@ describe('Thread screen', () => {
it('Can report the root post', async () => {
const post = by.id('postThreadItem-by-bob.test')
await element(by.id('postDropdownBtn').withAncestor(post)).atIndex(0).tap()
await element(by.id('postDropdownReportBtn')).tap()
await expect(element(by.id('reportPostModal'))).toBeVisible()
await element(by.id('reportPostRadios-spam')).tap()
await element(by.text('Report post')).tap()
await expect(element(by.id('reportModal'))).toBeVisible()
await element(
by.id('reportReasonRadios-com.atproto.moderation.defs#reasonSpam'),
).tap()
await element(by.id('sendReportBtn')).tap()
await expect(element(by.id('reportPostModal'))).not.toBeVisible()
await expect(element(by.id('reportModal'))).not.toBeVisible()
})
it('Can report a reply post', async () => {
const post = by.id('postThreadItem-by-carla.test')
await element(by.id('postDropdownBtn').withAncestor(post)).atIndex(0).tap()
await element(by.id('postDropdownReportBtn')).tap()
await expect(element(by.id('reportPostModal'))).toBeVisible()
await element(by.id('reportPostRadios-spam')).tap()
await element(by.text('Report post')).tap()
await expect(element(by.id('reportModal'))).toBeVisible()
await element(
by.id('reportReasonRadios-com.atproto.moderation.defs#reasonSpam'),
).tap()
await element(by.id('sendReportBtn')).tap()
await expect(element(by.id('reportPostModal'))).not.toBeVisible()
await expect(element(by.id('reportModal'))).not.toBeVisible()
})
})
+23
View File
@@ -1,10 +1,24 @@
import {resolveConfig} from 'detox/internals'
import {execSync} from 'child_process'
const platform = device.getPlatform()
export async function openApp(opts: any) {
opts = opts || {}
const config = await resolveConfig()
if (device.getPlatform() === 'ios') {
// disable password autofill
execSync(
`plutil -replace restrictedBool.allowPasswordAutoFill.value -bool NO ~/Library/Developer/CoreSimulator/Devices/${device.id}/data/Containers/Shared/SystemGroup/systemgroup.com.apple.configurationprofiles/Library/ConfigurationProfiles/UserSettings.plist`,
)
execSync(
`plutil -replace restrictedBool.allowPasswordAutoFill.value -bool NO ~/Library/Developer/CoreSimulator/Devices/${device.id}/data/Library/UserConfigurationProfiles/EffectiveUserSettings.plist`,
)
execSync(
`plutil -replace restrictedBool.allowPasswordAutoFill.value -bool NO ~/Library/Developer/CoreSimulator/Devices/${device.id}/data/Library/UserConfigurationProfiles/PublicInfo/PublicEffectiveUserSettings.plist`,
)
}
if (config.configurationName.split('.').includes('debug')) {
return await openAppForDebugBuild(platform, opts)
} else {
@@ -42,6 +56,7 @@ export async function login(
await device.takeScreenshot('2- opened service selector')
}
await element(by.id('customServerTextInput')).typeText(service)
await element(by.id('customServerTextInput')).tapReturnKey()
await element(by.id('customServerSelectBtn')).tap()
if (takeScreenshots) {
await device.takeScreenshot('3- input custom service')
@@ -54,6 +69,14 @@ export async function login(
await element(by.id('loginNextButton')).tap()
}
export async function loginAsAlice() {
await element(by.id('e2eSignInAlice')).tap()
}
export async function loginAsBob() {
await element(by.id('e2eSignInBob')).tap()
}
async function openAppForDebugBuild(platform: string, opts: any) {
const deepLinkUrl = // Local testing with packager
/*process.env.EXPO_USE_UPDATES
+5 -2
View File
@@ -1,4 +1,4 @@
import React from 'react'
import React, {ReactNode} from 'react'
import {View, ScrollView, Modal, FlatList, TextInput} from 'react-native'
const BottomSheetModalContext = React.createContext(null)
@@ -6,7 +6,10 @@ const BottomSheetModalContext = React.createContext(null)
const BottomSheetModalProvider = (props: any) => {
return <BottomSheetModalContext.Provider {...props} value={{}} />
}
class BottomSheet extends React.Component<{onClose?: () => void}> {
class BottomSheet extends React.Component<{
onClose?: () => void
children?: ReactNode
}> {
snapToIndex() {}
snapToPosition() {}
expand() {}
+1
View File
@@ -0,0 +1 @@
export const Slider = {}
+1
View File
@@ -0,0 +1 @@
export const getLocales = jest.fn().mockResolvedValue([])
+3
View File
@@ -0,0 +1,3 @@
jest.mock('sentry-expo', () => ({
init: () => jest.fn(),
}))
+2
View File
@@ -0,0 +1,2 @@
export const DropdownMenu = jest.fn().mockImplementation(() => {})
export const create = jest.fn().mockImplementation(() => {})
+1 -103
View File
@@ -1,106 +1,4 @@
import {
LikelyType,
getLinkMeta,
getLikelyType,
} from '../../src/lib/link-meta/link-meta'
import {exampleComHtml} from './__mocks__/exampleComHtml'
import {BskyAgent} from '@atproto/api'
import {DEFAULT_SERVICE, RootStoreModel} from '../../src/state'
describe('getLinkMeta', () => {
let rootStore: RootStoreModel
beforeEach(() => {
rootStore = new RootStoreModel(new BskyAgent({service: DEFAULT_SERVICE}))
})
const inputs = [
'',
'httpbadurl',
'https://example.com',
'https://example.com/index.html',
'https://example.com/image.png',
'https://example.com/video.avi',
'https://example.com/audio.ogg',
'https://example.com/text.txt',
'https://example.com/javascript.js',
'https://bsky.app/',
'https://bsky.app/index.html',
]
const outputs = [
{
error: 'Invalid URL',
likelyType: LikelyType.Other,
url: '',
},
{
error: 'Invalid URL',
likelyType: LikelyType.Other,
url: 'httpbadurl',
},
{
likelyType: LikelyType.HTML,
url: 'https://example.com',
title: 'Example Domain',
description: 'An example website',
},
{
likelyType: LikelyType.HTML,
url: 'https://example.com/index.html',
title: 'Example Domain',
description: 'An example website',
},
{
likelyType: LikelyType.Image,
url: 'https://example.com/image.png',
},
{
likelyType: LikelyType.Video,
url: 'https://example.com/video.avi',
},
{
likelyType: LikelyType.Audio,
url: 'https://example.com/audio.ogg',
},
{
likelyType: LikelyType.Text,
url: 'https://example.com/text.txt',
},
{
likelyType: LikelyType.Other,
url: 'https://example.com/javascript.js',
},
{
likelyType: LikelyType.AtpData,
url: '/',
},
{
likelyType: LikelyType.AtpData,
url: '/index.html',
},
{
likelyType: LikelyType.Other,
url: '',
title: '',
},
]
it('correctly handles a set of text inputs', async () => {
for (let i = 0; i < inputs.length; i++) {
global.fetch = jest.fn().mockImplementationOnce(() => {
return new Promise((resolve, _reject) => {
resolve({
ok: true,
status: 200,
text: () => exampleComHtml,
})
})
})
const input = inputs[i]
const output = await getLinkMeta(rootStore, input)
expect(output).toEqual(outputs[i])
}
})
})
import {LikelyType, getLikelyType} from '../../src/lib/link-meta/link-meta'
describe('getLikelyType', () => {
it('correctly handles non-parsed url', async () => {
+62 -4
View File
@@ -1,3 +1,4 @@
import {RichText} from '@atproto/api'
import {
getYoutubeVideoId,
makeRecordUri,
@@ -8,6 +9,7 @@ import {
import {pluralize, enforceLen} from '../../src/lib/strings/helpers'
import {ago} from '../../src/lib/strings/time'
import {detectLinkables} from '../../src/lib/strings/rich-text-detection'
import {shortenLinks} from '../../src/lib/strings/rich-text-manip'
import {makeValidHandle, createFullHandle} from '../../src/lib/strings/handles'
import {cleanError} from '../../src/lib/strings/errors'
@@ -48,7 +50,7 @@ describe('detectLinkables', () => {
'Classic article https://socket3.wordpress.com/2018/02/03/designing-windows-95s-user-interface/ ',
'https://foo.com https://bar.com/whatever https://baz.com',
'punctuation https://foo.com, https://bar.com/whatever; https://baz.com.',
'parenthentical (https://foo.com)',
'parenthetical (https://foo.com)',
'except for https://foo.com/thing_(cool)',
]
const outputs = [
@@ -112,7 +114,7 @@ describe('detectLinkables', () => {
{link: 'https://baz.com'},
'.',
],
['parenthentical (', {link: 'https://foo.com'}, ')'],
['parenthetical (', {link: 'https://foo.com'}, ')'],
['except for ', {link: 'https://foo.com/thing_(cool)'}],
]
it('correctly handles a set of text inputs', () => {
@@ -173,6 +175,7 @@ describe('ago', () => {
1671461038,
'04 Dec 1995 00:12:00 GMT',
new Date(),
new Date().setSeconds(new Date().getSeconds() - 10),
new Date().setMinutes(new Date().getMinutes() - 10),
new Date().setHours(new Date().getHours() - 1),
new Date().setDate(new Date().getDate() - 1),
@@ -181,7 +184,8 @@ describe('ago', () => {
const outputs = [
new Date(1671461038).toLocaleDateString(),
new Date('04 Dec 1995 00:12:00 GMT').toLocaleDateString(),
'0s',
'now',
'10s',
'10m',
'1h',
'1d',
@@ -296,11 +300,15 @@ describe('toShortUrl', () => {
'https://bsky.app',
'https://bsky.app/3jk7x4irgv52r',
'https://bsky.app/3jk7x4irgv52r2313y182h9',
'https://very-long-domain-name.com/foo',
'https://very-long-domain-name.com/foo?bar=baz#andsomemore',
]
const outputs = [
'bsky.app',
'bsky.app/3jk7x4irgv52r',
'bsky.app/3jk7x4irgv52r2313y...',
'bsky.app/3jk7x4irgv52...',
'very-long-domain-name.com/foo',
'very-long-domain-name.com/foo?bar=baz#...',
]
it('shortens the url', () => {
@@ -352,3 +360,53 @@ describe('getYoutubeVideoId', () => {
expect(getYoutubeVideoId('https://youtu.be/videoId')).toBe('videoId')
})
})
describe('shortenLinks', () => {
const inputs = [
'start https://middle.com/foo/bar?baz=bux#hash end',
'https://start.com/foo/bar?baz=bux#hash middle end',
'start middle https://end.com/foo/bar?baz=bux#hash',
'https://newline1.com/very/long/url/here\nhttps://newline2.com/very/long/url/here',
'Classic article https://socket3.wordpress.com/2018/02/03/designing-windows-95s-user-interface/',
]
const outputs = [
[
'start middle.com/foo/bar?baz=... end',
['https://middle.com/foo/bar?baz=bux#hash'],
],
[
'start.com/foo/bar?baz=... middle end',
['https://start.com/foo/bar?baz=bux#hash'],
],
[
'start middle end.com/foo/bar?baz=...',
['https://end.com/foo/bar?baz=bux#hash'],
],
[
'newline1.com/very/long/ur...\nnewline2.com/very/long/ur...',
[
'https://newline1.com/very/long/url/here',
'https://newline2.com/very/long/url/here',
],
],
[
'Classic article socket3.wordpress.com/2018/02/03/d...',
[
'https://socket3.wordpress.com/2018/02/03/designing-windows-95s-user-interface/',
],
],
]
it('correctly shortens rich text while preserving facet URIs', () => {
for (let i = 0; i < inputs.length; i++) {
const input = inputs[i]
const inputRT = new RichText({text: input})
inputRT.detectFacetsWithoutResolution()
const outputRT = shortenLinks(inputRT)
expect(outputRT.text).toEqual(outputs[i][0])
expect(outputRT.facets?.length).toEqual(outputs[i][1].length)
for (let j = 0; j < outputs[i][1].length; j++) {
expect(outputRT.facets![j].features[0].uri).toEqual(outputs[i][1][j])
}
}
})
})
+134
View File
@@ -0,0 +1,134 @@
import {
linkRequiresWarning,
isPossiblyAUrl,
splitApexDomain,
} from '../../../src/lib/strings/url-helpers'
describe('linkRequiresWarning', () => {
type Case = [string, string, boolean]
const cases: Case[] = [
['http://example.com', 'http://example.com', false],
['http://example.com', 'example.com', false],
['http://example.com', 'example.com/page', false],
['http://example.com', '', true],
['http://example.com', 'other.com', true],
['http://example.com', 'http://other.com', true],
['http://example.com', 'some label', true],
['http://example.com', 'example.com more', true],
['http://example.com', 'http://example.co', true],
['http://example.co', 'http://example.com', true],
['http://example.com', 'example.co', true],
['http://example.co', 'example.com', true],
['http://site.pages.dev', 'http://site.page', true],
['http://site.page', 'http://site.pages.dev', true],
['http://site.pages.dev', 'site.page', true],
['http://site.page', 'site.pages.dev', true],
['http://site.pages.dev', 'http://site.pages', true],
['http://site.pages', 'http://site.pages.dev', true],
['http://site.pages.dev', 'site.pages', true],
['http://site.pages', 'site.pages.dev', true],
['http://bsky.app/profile/bob.test/post/3kbeuduu7m22v', 'my post', false],
['https://bsky.app/profile/bob.test/post/3kbeuduu7m22v', 'my post', false],
['http://bsky.app/', 'bluesky', false],
['https://bsky.app/', 'bluesky', false],
[
'http://bsky.app/profile/bob.test/post/3kbeuduu7m22v',
'http://bsky.app/profile/bob.test/post/3kbeuduu7m22v',
false,
],
[
'https://bsky.app/profile/bob.test/post/3kbeuduu7m22v',
'http://bsky.app/profile/bob.test/post/3kbeuduu7m22v',
false,
],
[
'http://bsky.app/',
'http://bsky.app/profile/bob.test/post/3kbeuduu7m22v',
false,
],
[
'https://bsky.app/',
'http://bsky.app/profile/bob.test/post/3kbeuduu7m22v',
false,
],
[
'http://bsky.app/profile/bob.test/post/3kbeuduu7m22v',
'https://google.com',
true,
],
[
'https://bsky.app/profile/bob.test/post/3kbeuduu7m22v',
'https://google.com',
true,
],
['http://bsky.app/', 'https://google.com', true],
['https://bsky.app/', 'https://google.com', true],
// bad uri inputs, default to true
['', '', true],
['example.com', 'example.com', true],
]
it.each(cases)(
'given input uri %p and text %p, returns %p',
(uri, text, expected) => {
const output = linkRequiresWarning(uri, text)
expect(output).toEqual(expected)
},
)
})
describe('isPossiblyAUrl', () => {
type Case = [string, boolean]
const cases: Case[] = [
['', false],
['text', false],
['some text', false],
['some text', false],
['some domain.com', false],
['domain.com', true],
[' domain.com', true],
['domain.com ', true],
[' domain.com ', true],
['http://domain.com', true],
[' http://domain.com', true],
['http://domain.com ', true],
[' http://domain.com ', true],
['https://domain.com', true],
[' https://domain.com', true],
['https://domain.com ', true],
[' https://domain.com ', true],
['http://domain.com/foo', true],
['http://domain.com stuff', true],
]
it.each(cases)('given input uri %p, returns %p', (str, expected) => {
const output = isPossiblyAUrl(str)
expect(output).toEqual(expected)
})
})
describe('splitApexDomain', () => {
type Case = [string, string, string]
const cases: Case[] = [
['', '', ''],
['example.com', '', 'example.com'],
['foo.example.com', 'foo.', 'example.com'],
['foo.bar.example.com', 'foo.bar.', 'example.com'],
['example.co.uk', '', 'example.co.uk'],
['foo.example.co.uk', 'foo.', 'example.co.uk'],
['example.nonsense', '', 'example.nonsense'],
['foo.example.nonsense', '', 'foo.example.nonsense'],
['foo.bar.example.nonsense', '', 'foo.bar.example.nonsense'],
['example.com.example.com', 'example.com.', 'example.com'],
]
it.each(cases)(
'given input uri %p, returns %p,%p',
(str, expected1, expected2) => {
const output = splitApexDomain(str)
expect(output[0]).toEqual(expected1)
expect(output[1]).toEqual(expected2)
},
)
})
+114
View File
@@ -0,0 +1,114 @@
module.exports = function () {
const hasSentryToken = !!process.env.SENTRY_AUTH_TOKEN
return {
expo: {
name: 'Bluesky',
slug: 'bluesky',
scheme: 'bluesky',
owner: 'blueskysocial',
version: '1.56.0',
runtimeVersion: {
policy: 'appVersion',
},
orientation: 'portrait',
icon: './assets/icon.png',
userInterfaceStyle: 'automatic',
splash: {
image: './assets/cloud-splash.png',
resizeMode: 'cover',
backgroundColor: '#ffffff',
},
ios: {
buildNumber: '3',
supportsTablet: false,
bundleIdentifier: 'xyz.blueskyweb.app',
config: {
usesNonExemptEncryption: false,
},
infoPlist: {
UIBackgroundModes: ['remote-notification'],
NSCameraUsageDescription:
'Used for profile pictures, posts, and other kinds of content.',
NSMicrophoneUsageDescription:
'Used for posts and other kinds of content.',
NSPhotoLibraryAddUsageDescription:
'Used to save images to your library.',
NSPhotoLibraryUsageDescription:
'Used for profile pictures, posts, and other kinds of content',
},
associatedDomains: ['applinks:bsky.app', 'applinks:staging.bsky.app'],
},
androidStatusBar: {
barStyle: 'dark-content',
backgroundColor: '#ffffff',
},
android: {
versionCode: 46,
adaptiveIcon: {
foregroundImage: './assets/adaptive-icon.png',
backgroundColor: '#ffffff',
},
googleServicesFile: './google-services.json',
package: 'xyz.blueskyweb.app',
intentFilters: [
{
action: 'VIEW',
autoVerify: true,
data: [
{
scheme: 'https',
host: 'bsky.app',
},
],
category: ['BROWSABLE', 'DEFAULT'],
},
],
},
web: {
favicon: './assets/favicon.png',
},
updates: {
enabled: true,
fallbackToCacheTimeout: 1000,
url: 'https://u.expo.dev/55bd077a-d905-4184-9c7f-94789ba0f302',
},
plugins: [
'expo-localization',
hasSentryToken && 'sentry-expo',
[
'expo-build-properties',
{
android: {
compileSdkVersion: 34,
targetSdkVersion: 34,
buildToolsVersion: '34.0.0',
kotlinVersion: '1.8.0',
},
},
],
[
'expo-updates',
{
username: 'blueskysocial',
},
],
].filter(Boolean),
extra: {
eas: {
projectId: '55bd077a-d905-4184-9c7f-94789ba0f302',
},
},
hooks: {
postPublish: [
{
file: 'sentry-expo/upload-sourcemaps',
config: {
organization: 'blueskyweb',
project: 'react-native',
},
},
],
},
},
}
}
-69
View File
@@ -1,69 +0,0 @@
{
"expo": {
"name": "Bluesky",
"slug": "bluesky",
"owner": "blueskysocial",
"version": "1.15.0",
"orientation": "portrait",
"icon": "./assets/icon.png",
"userInterfaceStyle": "light",
"splash": {
"image": "./assets/cloud-splash.png",
"resizeMode": "cover",
"backgroundColor": "#ffffff"
},
"ios": {
"supportsTablet": false,
"bundleIdentifier": "xyz.blueskyweb.app",
"config": {
"usesNonExemptEncryption": false
},
"infoPlist": {
"UIBackgroundModes": [
"fetch",
"processing"
],
"BGTaskSchedulerPermittedIdentifiers": [
"com.transistorsoft.fetch"
]
}
},
"androidStatusBar": {
"barStyle": "dark-content",
"backgroundColor": "#ffffff"
},
"android": {
"adaptiveIcon": {
"foregroundImage": "./assets/adaptive-icon.png",
"backgroundColor": "#ffffff"
},
"package": "xyz.blueskyweb.app"
},
"web": {
"favicon": "./assets/favicon.png"
},
"plugins": [
"expo-localization",
"react-native-background-fetch",
[
"expo-build-properties",
{
"android": {
"kotlinVersion": "1.8.0"
}
}
],
[
"expo-updates",
{
"username": "blueskysocial"
}
]
],
"extra": {
"eas": {
"projectId": "55bd077a-d905-4184-9c7f-94789ba0f302"
}
}
}
}
+24 -1
View File
@@ -1,7 +1,23 @@
module.exports = function (api) {
api.cache(true)
const isTestEnv = process.env.NODE_ENV === 'test'
return {
presets: ['babel-preset-expo'],
presets: [
[
'babel-preset-expo',
{
lazyImports: true,
native: {
// We should be able to remove this after upgrading Expo
// to a version that includes https://github.com/expo/expo/pull/24672.
unstable_transformProfile: 'hermes-stable',
// Disable ESM -> CJS compilation because Metro takes care of it.
// However, we need it in Jest tests since those run without Metro.
disableImportExportTransform: !isTestEnv,
},
},
],
],
plugins: [
[
'module:react-native-dotenv',
@@ -21,6 +37,7 @@ module.exports = function (api) {
{
alias: {
// This needs to be mirrored in tsconfig.json
'#': './src',
lib: './src/lib',
platform: './src/platform',
state: './src/state',
@@ -28,7 +45,13 @@ module.exports = function (api) {
},
},
],
'macros',
'react-native-reanimated/plugin', // NOTE: this plugin MUST be last
],
env: {
production: {
plugins: ['transform-remove-console'],
},
},
}
}
+6
View File
@@ -4,5 +4,11 @@ test-coverage.out
# Don't check in the binary.
/bskyweb
# Don't accidentally commit JS-generated code
static/js/*.js
static/js/*.map
static/js/*.js.LICENSE.txt
templates/scripts.html
# Don't ignore this file
!.gitignore
+4 -1
View File
@@ -2,6 +2,9 @@
SHELL = /bin/bash
.SHELLFLAGS = -o pipefail -c
# https://github.com/golang/go/wiki/LoopvarExperiment
export GOEXPERIMENT := loopvar
.PHONY: help
help: ## Print info about all commands
@echo "Commands:"
@@ -39,4 +42,4 @@ check: ## Compile everything, checking syntax (does not output binaries)
.PHONY: run-dev-bskyweb
run-dev-bskyweb: .env ## Runs 'bskyweb' for local dev
GOLOG_LOG_LEVEL=info go run ./cmd/bskyweb serve
GOLOG_LOG_LEVEL=info go run ./cmd/bskyweb serve --debug
+2 -2
View File
@@ -2,7 +2,7 @@
### SPA Bundle (monolithic static javascript file)
To build the SPA bundle (`bundle.web.js`), first get a Javascript development
To build the SPA bundle (`bundle.web.js`), first get a JavaScript development
environment set up. Either follow the top-level README, or something quick
like:
@@ -24,7 +24,7 @@ Then build and copy over the big 'ol `bundle.web.js` file:
### Golang Daemon
Install golang. We are generally using v1.20+.
Install golang. We are generally using v1.21+.
In this directory (`bskyweb/`):
+1
View File
@@ -0,0 +1 @@
/bskyweb
+5 -3
View File
@@ -14,13 +14,15 @@ type Mailmodo struct {
httpClient *http.Client
APIKey string
BaseURL string
ListName string
}
func NewMailmodo(apiKey string) *Mailmodo {
func NewMailmodo(apiKey, listName string) *Mailmodo {
return &Mailmodo{
APIKey: apiKey,
BaseURL: "https://api.mailmodo.com/api/v1",
httpClient: &http.Client{},
ListName: listName,
}
}
@@ -56,9 +58,9 @@ func (m *Mailmodo) request(ctx context.Context, httpMethod string, apiMethod str
return nil
}
func (m *Mailmodo) AddToList(ctx context.Context, listName, email string) error {
func (m *Mailmodo) AddToList(ctx context.Context, email string) error {
return m.request(ctx, "POST", "addToList", map[string]any{
"listName": listName,
"listName": m.ListName,
"email": email,
"data": map[string]any{
"email_hashed": fmt.Sprintf("%x", sha256.Sum256([]byte(email))),
+5 -16
View File
@@ -34,22 +34,11 @@ func run(args []string) {
Action: serve,
Flags: []cli.Flag{
&cli.StringFlag{
Name: "pds-host",
Usage: "method, hostname, and port of PDS instance",
Value: "http://localhost:4849",
EnvVars: []string{"ATP_PDS_HOST"},
},
&cli.StringFlag{
Name: "handle",
Usage: "for PDS login",
Required: true,
EnvVars: []string{"ATP_AUTH_HANDLE"},
},
&cli.StringFlag{
Name: "password",
Usage: "for PDS login",
Required: true,
EnvVars: []string{"ATP_AUTH_PASSWORD"},
Name: "appview-host",
Usage: "method, hostname, and port of PDS instance",
Value: "http://localhost:2584",
// retain old PDS env var for easy transition
EnvVars: []string{"ATP_APPVIEW_HOST", "ATP_PDS_HOST"},
},
&cli.StringFlag{
Name: "mailmodo-api-key",
+229 -48
View File
@@ -2,66 +2,146 @@ package main
import (
"context"
"encoding/json"
"errors"
"fmt"
"io/fs"
"io/ioutil"
"net/http"
"os"
"os/signal"
"strings"
"syscall"
"time"
comatproto "github.com/bluesky-social/indigo/api/atproto"
appbsky "github.com/bluesky-social/indigo/api/bsky"
cliutil "github.com/bluesky-social/indigo/cmd/gosky/util"
"github.com/bluesky-social/indigo/xrpc"
"github.com/bluesky-social/social-app/bskyweb"
"github.com/flosch/pongo2/v6"
"github.com/klauspost/compress/gzhttp"
"github.com/klauspost/compress/gzip"
"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
"github.com/urfave/cli/v2"
)
type Server struct {
xrpcc *xrpc.Client
echo *echo.Echo
httpd *http.Server
mailmodo *Mailmodo
xrpcc *xrpc.Client
}
func serve(cctx *cli.Context) error {
debug := cctx.Bool("debug")
httpAddress := cctx.String("http-address")
pdsHost := cctx.String("pds-host")
atpHandle := cctx.String("handle")
atpPassword := cctx.String("password")
appviewHost := cctx.String("appview-host")
mailmodoAPIKey := cctx.String("mailmodo-api-key")
mailmodoListName := cctx.String("mailmodo-list-name")
// Mailmodo client.
mailmodo := NewMailmodo(mailmodoAPIKey)
// Echo
e := echo.New()
// create a new session
// TODO: does this work with no auth at all?
// Mailmodo client.
mailmodo := NewMailmodo(mailmodoAPIKey, mailmodoListName)
// create a new session (no auth)
xrpcc := &xrpc.Client{
Client: cliutil.NewHttpClient(),
Host: pdsHost,
Auth: &xrpc.AuthInfo{
Handle: atpHandle,
},
Host: appviewHost,
}
auth, err := comatproto.ServerCreateSession(context.TODO(), xrpcc, &comatproto.ServerCreateSession_Input{
Identifier: &xrpcc.Auth.Handle,
Password: atpPassword,
})
// httpd
var (
httpTimeout = 2 * time.Minute
httpMaxHeaderBytes = 2 * (1024 * 1024)
gzipMinSizeBytes = 1024 * 2
gzipCompressionLevel = gzip.BestSpeed
gzipExceptMIMETypes = []string{"image/png"}
)
// Wrap the server handler in a gzip handler to compress larger responses.
gzipHandler, err := gzhttp.NewWrapper(
gzhttp.MinSize(gzipMinSizeBytes),
gzhttp.CompressionLevel(gzipCompressionLevel),
gzhttp.ExceptContentTypes(gzipExceptMIMETypes),
)
if err != nil {
return err
}
xrpcc.Auth.AccessJwt = auth.AccessJwt
xrpcc.Auth.RefreshJwt = auth.RefreshJwt
xrpcc.Auth.Did = auth.Did
xrpcc.Auth.Handle = auth.Handle
server := Server{xrpcc}
//
// server
//
server := &Server{
echo: e,
mailmodo: mailmodo,
xrpcc: xrpcc,
}
// Create the HTTP server.
server.httpd = &http.Server{
Handler: gzipHandler(server),
Addr: httpAddress,
WriteTimeout: httpTimeout,
ReadTimeout: httpTimeout,
MaxHeaderBytes: httpMaxHeaderBytes,
}
e.HideBanner = true
e.Renderer = NewRenderer("templates/", &bskyweb.TemplateFS, debug)
e.HTTPErrorHandler = server.errorHandler
e.IPExtractor = echo.ExtractIPFromXFFHeader()
// SECURITY: Do not modify without due consideration.
e.Use(middleware.SecureWithConfig(middleware.SecureConfig{
ContentTypeNosniff: "nosniff",
XFrameOptions: "SAMEORIGIN",
HSTSMaxAge: 31536000, // 365 days
// TODO:
// ContentSecurityPolicy
// XSSProtection
}))
e.Use(middleware.LoggerWithConfig(middleware.LoggerConfig{
// Don't log requests for static content.
Skipper: func(c echo.Context) bool {
return strings.HasPrefix(c.Request().URL.Path, "/static")
},
}))
e.Use(middleware.RateLimiterWithConfig(middleware.RateLimiterConfig{
Skipper: middleware.DefaultSkipper,
Store: middleware.NewRateLimiterMemoryStoreWithConfig(
middleware.RateLimiterMemoryStoreConfig{
Rate: 10, // requests per second
Burst: 30, // allow bursts
ExpiresIn: 3 * time.Minute, // garbage collect entries older than 3 minutes
},
),
IdentifierExtractor: func(ctx echo.Context) (string, error) {
id := ctx.RealIP()
return id, nil
},
DenyHandler: func(c echo.Context, identifier string, err error) error {
return c.String(http.StatusTooManyRequests, "Your request has been rate limited. Please try again later. Contact security@bsky.app if you believe this was a mistake.\n")
},
}))
// redirect trailing slash to non-trailing slash.
// all of our current endpoints have no trailing slash.
e.Use(middleware.RemoveTrailingSlashWithConfig(middleware.TrailingSlashConfig{
RedirectCode: http.StatusFound,
}))
//
// configure routes
//
// static files
staticHandler := http.FileServer(func() http.FileSystem {
if debug {
log.Debugf("serving static file from the local file system")
return http.FS(os.DirFS("static"))
}
fsys, err := fs.Sub(bskyweb.StaticFS, "static")
@@ -71,57 +151,117 @@ func serve(cctx *cli.Context) error {
return http.FS(fsys)
}())
e := echo.New()
e.HideBanner = true
e.Use(middleware.LoggerWithConfig(middleware.LoggerConfig{
// Don't log requests for static content.
Skipper: func(c echo.Context) bool {
return strings.HasPrefix(c.Request().URL.Path, "/static")
},
Format: "method=${method} path=${uri} status=${status} latency=${latency_human}\n",
}))
e.Renderer = NewRenderer("templates/", &bskyweb.TemplateFS, debug)
e.HTTPErrorHandler = customHTTPErrorHandler
// configure routes
e.GET("/robots.txt", echo.WrapHandler(staticHandler))
e.GET("/static/*", echo.WrapHandler(http.StripPrefix("/static/", staticHandler)))
e.GET("/ips-v4", echo.WrapHandler(staticHandler))
e.GET("/ips-v6", echo.WrapHandler(staticHandler))
e.GET("/.well-known/*", echo.WrapHandler(staticHandler))
e.GET("/security.txt", func(c echo.Context) error {
return c.Redirect(http.StatusMovedPermanently, "/.well-known/security.txt")
})
e.GET("/static/*", echo.WrapHandler(http.StripPrefix("/static/", staticHandler)), func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
path := c.Request().URL.Path
maxAge := 1 * (60 * 60) // default is 1 hour
// Cache javascript and images files for 1 week, which works because
// they're always versioned (e.g. /static/js/main.64c14927.js)
if strings.HasPrefix(path, "/static/js/") || strings.HasPrefix(path, "/static/images/") {
maxAge = 7 * (60 * 60 * 24) // 1 week
}
c.Response().Header().Set("Cache-Control", fmt.Sprintf("public, max-age=%d", maxAge))
return next(c)
}
})
// home
e.GET("/", server.WebHome)
// generic routes
e.GET("/search", server.WebGeneric)
e.GET("/feeds", server.WebGeneric)
e.GET("/notifications", server.WebGeneric)
e.GET("/lists", server.WebGeneric)
e.GET("/moderation", server.WebGeneric)
e.GET("/moderation/modlists", server.WebGeneric)
e.GET("/moderation/muted-accounts", server.WebGeneric)
e.GET("/moderation/blocked-accounts", server.WebGeneric)
e.GET("/settings", server.WebGeneric)
e.GET("/settings/language", server.WebGeneric)
e.GET("/settings/app-passwords", server.WebGeneric)
e.GET("/settings/home-feed", server.WebGeneric)
e.GET("/settings/saved-feeds", server.WebGeneric)
e.GET("/settings/threads", server.WebGeneric)
e.GET("/sys/debug", server.WebGeneric)
e.GET("/sys/log", server.WebGeneric)
e.GET("/support", server.WebGeneric)
e.GET("/support/privacy", server.WebGeneric)
e.GET("/support/tos", server.WebGeneric)
e.GET("/support/community-guidelines", server.WebGeneric)
e.GET("/support/copyright", server.WebGeneric)
// profile endpoints; only first populates info
e.GET("/profile/:handle", server.WebProfile)
e.GET("/profile/:handle/follows", server.WebGeneric)
e.GET("/profile/:handle/followers", server.WebGeneric)
e.GET("/profile/:handle/lists/:rkey", server.WebGeneric)
e.GET("/profile/:handle/feed/:rkey", server.WebGeneric)
e.GET("/profile/:handle/feed/:rkey/liked-by", server.WebGeneric)
// post endpoints; only first populates info
e.GET("/profile/:handle/post/:rkey", server.WebPost)
e.GET("/profile/:handle/post/:rkey/upvoted-by", server.WebGeneric)
e.GET("/profile/:handle/post/:rkey/downvoted-by", server.WebGeneric)
e.GET("/profile/:handle/post/:rkey/liked-by", server.WebGeneric)
e.GET("/profile/:handle/post/:rkey/reposted-by", server.WebGeneric)
// Mailmodo
e.POST("/waitlist", func(c echo.Context) error {
email := strings.TrimSpace(c.FormValue("email"))
if err := mailmodo.AddToList(c.Request().Context(), mailmodoListName, email); err != nil {
return err
}
return c.JSON(http.StatusOK, map[string]bool{"success": true})
})
e.POST("/api/waitlist", server.apiWaitlist)
// Start the server.
log.Infof("starting server address=%s", httpAddress)
return e.Start(httpAddress)
go func() {
if err := server.httpd.ListenAndServe(); err != nil {
if !errors.Is(err, http.ErrServerClosed) {
log.Errorf("HTTP server shutting down unexpectedly: %s", err)
}
}
}()
// Wait for a signal to exit.
log.Info("registering OS exit signal handler")
quit := make(chan struct{})
exitSignals := make(chan os.Signal, 1)
signal.Notify(exitSignals, syscall.SIGINT, syscall.SIGTERM)
go func() {
sig := <-exitSignals
log.Infof("received OS exit signal: %s", sig)
// Shut down the HTTP server.
if err := server.Shutdown(); err != nil {
log.Errorf("HTTP server shutdown error: %s", err)
}
// Trigger the return that causes an exit.
close(quit)
}()
<-quit
log.Infof("graceful shutdown complete")
return nil
}
func customHTTPErrorHandler(err error, c echo.Context) {
func (srv *Server) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
srv.echo.ServeHTTP(rw, req)
}
func (srv *Server) Shutdown() error {
log.Info("shutting down")
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
return srv.httpd.Shutdown(ctx)
}
func (srv *Server) errorHandler(err error, c echo.Context) {
code := http.StatusInternalServerError
if he, ok := err.(*echo.HTTPError); ok {
code = he.Code
@@ -165,7 +305,13 @@ func (srv *Server) WebPost(c echo.Context) error {
if err != nil {
log.Warnf("failed to fetch post: %s\t%v", uri, err)
} else {
data["postView"] = tpv.Thread.FeedDefs_ThreadViewPost.Post
req := c.Request()
postView := tpv.Thread.FeedDefs_ThreadViewPost.Post
data["postView"] = postView
data["requestURI"] = fmt.Sprintf("https://%s%s", req.Host, req.URL.Path)
if postView.Embed != nil && postView.Embed.EmbedImages_View != nil {
data["imgThumbUrl"] = postView.Embed.EmbedImages_View.Images[0].Thumb
}
}
}
@@ -183,9 +329,44 @@ func (srv *Server) WebProfile(c echo.Context) error {
if err != nil {
log.Warnf("failed to fetch handle: %s\t%v", handle, err)
} else {
req := c.Request()
data["profileView"] = pv
data["requestURI"] = fmt.Sprintf("https://%s%s", req.Host, req.URL.Path)
}
}
return c.Render(http.StatusOK, "profile.html", data)
}
func (srv *Server) apiWaitlist(c echo.Context) error {
type jsonError struct {
Error string `json:"error"`
}
// Read the API request.
type apiRequest struct {
Email string `json:"email"`
}
bodyReader := http.MaxBytesReader(c.Response(), c.Request().Body, 16*1024)
payload, err := ioutil.ReadAll(bodyReader)
if err != nil {
return err
}
var req apiRequest
if err := json.Unmarshal(payload, &req); err != nil {
return c.JSON(http.StatusBadRequest, jsonError{Error: "Invalid API request"})
}
if req.Email == "" {
return c.JSON(http.StatusBadRequest, jsonError{Error: "Please enter a valid email address."})
}
if err := srv.mailmodo.AddToList(c.Request().Context(), req.Email); err != nil {
log.Errorf("adding email to waitlist failed: %s", err)
return c.JSON(http.StatusBadRequest, jsonError{
Error: "Storing email in waitlist failed. Please enter a valid email address.",
})
}
return c.JSON(http.StatusOK, map[string]bool{"success": true})
}
+1 -3
View File
@@ -1,4 +1,2 @@
GOLOG_LOG_LEVEL=info
ATP_PDS_HOST=http://localhost:2583
ATP_AUTH_HANDLE="admin.test"
ATP_AUTH_PASSWORD="admin"
ATP_APPVIEW_HOST=http://localhost:2584
+1 -3
View File
@@ -1,4 +1,2 @@
GOLOG_LOG_LEVEL=info
ATP_PDS_HOST=https://bsky.social
ATP_AUTH_HANDLE=TODO
ATP_AUTH_PASSWORD=TODO
ATP_APPVIEW_HOST=https://api.bsky.app
+16 -15
View File
@@ -1,20 +1,21 @@
module github.com/bluesky-social/social-app/bskyweb
go 1.20
go 1.21
require (
github.com/bluesky-social/indigo v0.0.0-20230403211508-3cb4320bd5c8
github.com/bluesky-social/indigo v0.0.0-20230504025040-8915cccc3319
github.com/flosch/pongo2/v6 v6.0.0
github.com/ipfs/go-log v1.0.5
github.com/joho/godotenv v1.5.1
github.com/klauspost/compress v1.16.5
github.com/labstack/echo/v4 v4.10.2
github.com/urfave/cli/v2 v2.25.1
github.com/urfave/cli/v2 v2.25.3
)
require (
github.com/benbjohnson/clock v1.3.0 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect
github.com/go-logr/logr v1.2.4 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/goccy/go-json v0.10.2 // indirect
@@ -26,7 +27,7 @@ require (
github.com/hashicorp/golang-lru v0.5.4 // indirect
github.com/ipfs/bbloom v0.0.4 // indirect
github.com/ipfs/go-block-format v0.1.2 // indirect
github.com/ipfs/go-cid v0.4.0 // indirect
github.com/ipfs/go-cid v0.4.1 // indirect
github.com/ipfs/go-datastore v0.6.0 // indirect
github.com/ipfs/go-ipfs-blockstore v1.3.0 // indirect
github.com/ipfs/go-ipfs-ds-help v1.1.0 // indirect
@@ -68,22 +69,22 @@ require (
github.com/spaolacci/murmur3 v1.1.0 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasttemplate v1.2.2 // indirect
github.com/whyrusleeping/cbor-gen v0.0.0-20230331140348-1f892b517e70 // indirect
github.com/whyrusleeping/cbor-gen v0.0.0-20230418232409-daab9ece03a0 // indirect
github.com/whyrusleeping/go-did v0.0.0-20230301193428-2146016fc220 // indirect
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
go.opentelemetry.io/otel v1.14.0 // indirect
go.opentelemetry.io/otel/trace v1.14.0 // indirect
go.uber.org/atomic v1.10.0 // indirect
go.opentelemetry.io/otel v1.15.1 // indirect
go.opentelemetry.io/otel/trace v1.15.1 // indirect
go.uber.org/atomic v1.11.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.24.0 // indirect
golang.org/x/crypto v0.7.0 // indirect
golang.org/x/net v0.8.0 // indirect
golang.org/x/sys v0.6.0 // indirect
golang.org/x/text v0.8.0 // indirect
golang.org/x/crypto v0.8.0 // indirect
golang.org/x/net v0.9.0 // indirect
golang.org/x/sys v0.7.0 // indirect
golang.org/x/text v0.9.0 // indirect
golang.org/x/time v0.3.0 // indirect
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
gorm.io/driver/postgres v1.5.0 // indirect
gorm.io/driver/sqlite v1.4.4 // indirect
gorm.io/gorm v1.24.7-0.20230306060331-85eaf9eeda11 // indirect
gorm.io/driver/sqlite v1.5.0 // indirect
gorm.io/gorm v1.25.0 // indirect
lukechampine.com/blake3 v1.1.7 // indirect
)
+32 -24
View File
@@ -2,8 +2,8 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A=
github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/bluesky-social/indigo v0.0.0-20230403211508-3cb4320bd5c8 h1:6A8D48CksjnlmJb8r5WaFAB4J2osllkmQoQhhiREMHw=
github.com/bluesky-social/indigo v0.0.0-20230403211508-3cb4320bd5c8/go.mod h1:9dcnKLtEnDPBdWm5/BLJHvbaPndCdKzAaBK7sbGt3S4=
github.com/bluesky-social/indigo v0.0.0-20230504025040-8915cccc3319 h1:VCNXRXpgyK3xkaQ8fzL5WzswerwLycke4B9ggLs1uOA=
github.com/bluesky-social/indigo v0.0.0-20230504025040-8915cccc3319/go.mod h1:Hc09SUJXAIujaAvq7JXxi8ZQQI887grzPkHgn4JyE1Q=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
@@ -12,8 +12,9 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 h1:HbphB4TFFXpv7MNrT52FGrrgVXF1owhMVTHFZIlnvd4=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0/go.mod h1:DZGJHZMqrU4JJqFAWUS2UO1+lbSKsdiOoYi9Zzey7Fc=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0=
github.com/flosch/pongo2/v6 v6.0.0 h1:lsGru8IAzHgIAw6H2m4PCyleO58I40ow6apih0WprMU=
github.com/flosch/pongo2/v6 v6.0.0/go.mod h1:CuDpFm47R0uGGE7z13/tTlt1Y6zdxvr2RLT5LJhsHEU=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
@@ -30,6 +31,7 @@ github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keL
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
@@ -55,8 +57,8 @@ github.com/ipfs/go-cid v0.0.1/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUP
github.com/ipfs/go-cid v0.0.2/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM=
github.com/ipfs/go-cid v0.0.5/go.mod h1:plgt+Y5MnOey4vO4UlUazGqdbEXuFYitED67FexhXog=
github.com/ipfs/go-cid v0.0.6/go.mod h1:6Ux9z5e+HpkQdckYoX1PG/6xqKspzlEIR5SDmgqgC/I=
github.com/ipfs/go-cid v0.4.0 h1:a4pdZq0sx6ZSxbCizebnKiMCx/xI/aBBFlB73IgH4rA=
github.com/ipfs/go-cid v0.4.0/go.mod h1:uQHwDeX4c6CtyrFwdqyhpNcxVewur1M7l7fNU7LKwZk=
github.com/ipfs/go-cid v0.4.1 h1:A/T3qGvxi4kpKWWcPC/PgbvDA2bjVLO7n4UeVwnbs/s=
github.com/ipfs/go-cid v0.4.1/go.mod h1:uQHwDeX4c6CtyrFwdqyhpNcxVewur1M7l7fNU7LKwZk=
github.com/ipfs/go-datastore v0.5.0/go.mod h1:9zhEApYMTl17C8YDp7JmU7sQZi2/wqiYh73hakZ90Bk=
github.com/ipfs/go-datastore v0.6.0 h1:JKyz+Gvz1QEZw0LsX1IBn+JFCJQH4SJVFtM4uWU0Myk=
github.com/ipfs/go-datastore v0.6.0/go.mod h1:rt5M3nNbSO/8q1t4LNkLyUwRs8HupMeN/8O4Vn9YAT8=
@@ -96,7 +98,6 @@ github.com/jbenet/goprocess v0.1.4 h1:DRGOFReOMqqDNXwW70QkacFW0YN9QnwLV0Vqk+3oU0
github.com/jbenet/goprocess v0.1.4/go.mod h1:5yspPrukOVuOLORacaBi858NqyClJPQxYZlqdZVfqY4=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
@@ -105,6 +106,8 @@ github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.16.5 h1:IFV2oUNUzZaz+XyusxpLzpzS8Pt5rh0Z16For/djlyI=
github.com/klauspost/compress v1.16.5/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk=
@@ -209,8 +212,8 @@ github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/urfave/cli v1.22.10/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/urfave/cli/v2 v2.25.1 h1:zw8dSP7ghX0Gmm8vugrs6q9Ku0wzweqPyshy+syu9Gw=
github.com/urfave/cli/v2 v2.25.1/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc=
github.com/urfave/cli/v2 v2.25.3 h1:VJkt6wvEBOoSjPFQvOkv6iWIrsJyCrKGtCtxXWwmGeY=
github.com/urfave/cli/v2 v2.25.3/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
@@ -218,8 +221,8 @@ github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQ
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
github.com/warpfork/go-wish v0.0.0-20220906213052-39a1cc7a02d0 h1:GDDkbFiaK8jsSDJfjId/PEGEShv6ugrt4kYsC5UIDaQ=
github.com/warpfork/go-wish v0.0.0-20220906213052-39a1cc7a02d0/go.mod h1:x6AKhvSSexNrVSrViXSHUEbICjmGXhtgABaHIySUSGw=
github.com/whyrusleeping/cbor-gen v0.0.0-20230331140348-1f892b517e70 h1:iNBzUKTsJc9RqStEVX2VYgVHATTU39IuB7g0e8OPWXU=
github.com/whyrusleeping/cbor-gen v0.0.0-20230331140348-1f892b517e70/go.mod h1:fgkXqYy7bV2cFeIEOkVTZS/WjXARfBqSH6Q2qHL33hQ=
github.com/whyrusleeping/cbor-gen v0.0.0-20230418232409-daab9ece03a0 h1:XYEgH2nJgsrcrj32p+SAbx6T3s/6QknOXezXtz7kzbg=
github.com/whyrusleeping/cbor-gen v0.0.0-20230418232409-daab9ece03a0/go.mod h1:fgkXqYy7bV2cFeIEOkVTZS/WjXARfBqSH6Q2qHL33hQ=
github.com/whyrusleeping/go-did v0.0.0-20230301193428-2146016fc220 h1:EO/9z3yDvx1van1/0esdcqhalZZQGRj3I1BPTWr5k3A=
github.com/whyrusleeping/go-did v0.0.0-20230301193428-2146016fc220/go.mod h1:qPtRyexGM5XMHFIfjH+EiA/A/1n2JakWEdMPC53pJAE=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
@@ -228,16 +231,17 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.opentelemetry.io/otel v1.14.0 h1:/79Huy8wbf5DnIPhemGB+zEPVwnN6fuQybr/SRXa6hM=
go.opentelemetry.io/otel v1.14.0/go.mod h1:o4buv+dJzx8rohcUeRmWUZhqupFvzWis188WlggnNeU=
go.opentelemetry.io/otel/trace v1.14.0 h1:wp2Mmvj41tDsyAJXiWDWpfNsOiIyd38fy85pyKcFq/M=
go.opentelemetry.io/otel/trace v1.14.0/go.mod h1:8avnQLK+CG77yNLUae4ea2JDQ6iT+gozhnZjy/rw9G8=
go.opentelemetry.io/otel v1.15.1 h1:3Iwq3lfRByPaws0f6bU3naAqOR1n5IeDWd9390kWHa8=
go.opentelemetry.io/otel v1.15.1/go.mod h1:mHHGEHVDLal6YrKMmk9LqC4a3sF5g+fHfrttQIB1NTc=
go.opentelemetry.io/otel/trace v1.15.1 h1:uXLo6iHJEzDfrNC0L0mNjItIp06SyaBQxu5t3xMlngY=
go.opentelemetry.io/otel/trace v1.15.1/go.mod h1:IWdQG/5N1x7f6YUlmdLeJvH9yxtuJAfc4VW5Agv9r/8=
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI=
go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
@@ -255,8 +259,9 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ=
golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
@@ -273,8 +278,9 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM=
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -297,8 +303,9 @@ golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
@@ -307,8 +314,9 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -343,11 +351,11 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/driver/postgres v1.5.0 h1:u2FXTy14l45qc3UeCJ7QaAXZmZfDDv0YrthvmRq1l0U=
gorm.io/driver/postgres v1.5.0/go.mod h1:FUZXzO+5Uqg5zzwzv4KK49R8lvGIyscBOqYrtI1Ce9A=
gorm.io/driver/sqlite v1.4.4 h1:gIufGoR0dQzjkyqDyYSCvsYR6fba1Gw5YKDqKeChxFc=
gorm.io/driver/sqlite v1.4.4/go.mod h1:0Aq3iPO+v9ZKbcdiz8gLWRw5VOPcBOPUQJFLq5e2ecI=
gorm.io/gorm v1.24.0/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA=
gorm.io/gorm v1.24.7-0.20230306060331-85eaf9eeda11 h1:9qNbmu21nNThCNnF5i2R3kw2aL27U8ZwbzccNjOmW0g=
gorm.io/driver/sqlite v1.5.0 h1:zKYbzRCpBrT1bNijRnxLDJWPjVfImGEn0lSnUY5gZ+c=
gorm.io/driver/sqlite v1.5.0/go.mod h1:kDMDfntV9u/vuMmz8APHtHF0b4nyBB7sfCieC6G8k8I=
gorm.io/gorm v1.24.7-0.20230306060331-85eaf9eeda11/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
gorm.io/gorm v1.25.0 h1:+KtYtb2roDz14EQe4bla8CbQlmb9dN3VejSai3lprfU=
gorm.io/gorm v1.25.0/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
lukechampine.com/blake3 v1.1.7 h1:GgRMhmdsuK8+ii6UZFDL8Nb+VyMwadAgcJyfYHxG6n0=
lukechampine.com/blake3 v1.1.7/go.mod h1:tkKEOtDkNtklkXtLNEOGNq5tcV90tJiA1vAA12R78LA=
@@ -0,0 +1,13 @@
{
"applinks": {
"apps": [],
"details": [
{
"appID": "B3LX46C5HS.xyz.blueskyweb.app",
"paths": [
"*"
]
}
]
}
}
@@ -0,0 +1,11 @@
[
{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "xyz.blueskyweb.app",
"sha256_cert_fingerprints":
["C1:4D:3C:6B:B5:D6:D9:AE:CF:C5:0B:BC:C1:9B:29:6D:D4:E6:87:46:36:D5:4C:1A:64:1C:14:08:BF:7E:F9:62", "FA:C6:17:45:DC:09:03:78:6F:B9:ED:E6:2A:96:2B:39:9F:73:48:F0:BB:6F:89:9B:83:32:66:75:91:03:3B:9C"]
}
}
]
+4
View File
@@ -0,0 +1,4 @@
Contact: mailto:security@bsky.app
Preferred-Languages: en
Canonical: https://bsky.app/.well-known/security.txt
Acknowledgements: https://github.com/bluesky-social/atproto/blob/main/CONTRIBUTORS.md
+30
View File
@@ -0,0 +1,30 @@
13.59.225.103/32
3.18.47.21/32
18.191.104.94/32
3.129.134.255/32
3.129.237.113/32
3.138.56.230/32
44.218.10.163/32
54.89.116.251/32
44.217.166.202/32
54.208.221.149/32
54.166.110.54/32
54.208.146.65/32
3.129.234.15/32
3.138.168.48/32
3.23.53.192/32
52.14.89.53/32
3.18.126.246/32
3.136.69.4/32
3.22.137.152/32
3.132.247.113/32
3.141.186.104/32
18.222.43.214/32
3.14.35.197/32
3.23.182.70/32
18.224.144.69/32
3.129.98.29/32
3.130.134.20/32
3.17.197.213/32
18.223.234.21/32
3.20.248.177/32
View File
+9 -1
View File
@@ -1 +1,9 @@
# hello friends!
# Hello Friends!
# If you are considering bulk or automated crawling, you may want to look in
# to our protocol (API), including a firehose of updates. See: https://atproto.com/
# By default, may crawl anything on this domain. HTTP 429 ("backoff") status
# codes are used for rate-limiting. Up to a handful concurrent requests should
# be ok.
User-Agent: *
Allow: /
+99 -55
View File
@@ -1,9 +1,8 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta httpEquiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1.00001, viewport-fit=cover">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, viewport-fit=cover">
<meta name="referrer" content="origin-when-cross-origin">
<title>{%- block head_title -%}Bluesky{%- endblock -%}</title>
@@ -40,6 +39,32 @@
height: calc(100% + env(safe-area-inset-top));
}
/* Color theming */
:root {
--text: black;
--background: white;
--backgroundLight: #F3F3F8;
}
html.colorMode--dark {
--text: white;
--background: black;
--backgroundLight: #26272D;
}
@media (prefers-color-scheme: light) {
html.colorMode--system {
--text: black;
--background: white;
--backgroundLight: #F3F3F8;
}
}
@media (prefers-color-scheme: dark) {
html.colorMode--system {
--text: white;
--background: black;
--backgroundLight: #26272D;
}
}
body {
display: flex;
/* Allows you to scroll below the viewport; default value is visible */
@@ -50,17 +75,7 @@
-moz-osx-font-smoothing: grayscale;
-ms-overflow-style: scrollbar;
}
/* Enable for apps that support dark-theme */
/*@media (prefers-color-scheme: dark) {
body {
background-color: black;
}
}*/
/* Remove focus state on inputs */
*:focus {
outline: 0;
}
/* Remove default link styling */
a {
color: inherit;
@@ -68,12 +83,26 @@
a[role="link"]:hover {
text-decoration: underline;
}
a[role="link"][data-no-underline="1"]:hover {
text-decoration: none;
}
/* Styling hacks */
*[data-word-wrap] {
word-break: break-word;
}
*[data-stable-gutters] {
scrollbar-gutter: stable both-edges;
}
/* ProseMirror */
.ProseMirror {
font: 18px -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
min-height: 140px;
}
.ProseMirror-dark {
color: white;
}
.ProseMirror p {
margin: 0;
}
@@ -87,62 +116,77 @@
.ProseMirror .mention {
color: #0085ff;
}
.ProseMirror a {
.ProseMirror a,
.ProseMirror .autolink {
color: #0085ff;
cursor: pointer;
}
/* OLLIE: TODO -- this is not accessible */
/* Remove focus state on inputs */
.ProseMirror-focused {
outline: 0;
}
textarea:focus,
input:focus {
outline: 0;
}
.tippy-content .items {
border-radius: 6px;
background: #F3F3F8;
border: 1px solid #e0d9d9;
padding: 3px 3px;
width: fit-content;
}
.tippy-content .items .item {
display: block;
background: transparent;
color: #8a8c9a;
border: 0;
font: 17px -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
padding: 7px 10px 8px;
width: 100%;
text-align: left;
box-sizing: border-box;
letter-spacing: 0.2px;
/* Tooltips */
[data-tooltip] {
position: relative;
z-index: 10;
}
.tippy-content .items .item.is-selected {
background: #fff;
border-radius: 4px;
color: #333;
[data-tooltip]::after {
content: attr(data-tooltip);
display: none;
position: absolute;
bottom: 0;
left: 50%;
transform: translateY(100%) translateY(8px) translateX(-50%);
padding: 4px 10px;
border-radius: 10px;
background: var(--backgroundLight);
color: var(--text);
text-align: center;
white-space: nowrap;
font-size: 12px;
z-index: 10;
}
[data-tooltip]::before {
content: '';
display: none;
position: absolute;
border-bottom: 6px solid var(--backgroundLight);
border-left: 6px solid transparent;
border-right: 6px solid transparent;
bottom: 0;
left: 50%;
transform: translateY(100%) translateY(2px) translateX(-50%);
z-index: 10;
}
[data-tooltip]:hover::after,
[data-tooltip]:hover::before {
display:block;
}
</style>
{% include "scripts.html" %}
<link rel="apple-touch-icon" sizes="180x180" href="/static/apple-touch-icon.png"/>
<link rel="icon" type="image/png" sizes="32x32" href="/static/favicon-32x32.png"/>
<link rel="icon" type="image/png" sizes="16x16" href="/static/favicon-16x16.png"/>
{% block head_page_meta -%}
<meta property="og:title" content="Bluesky Social"/>
<meta property="og:type" content="article"/>
<meta property="og:image" content="/static/default-social-card.png"/>
<meta name="twitter:title" content="Bluesky Social"/>
<meta name="twitter:description" content="See what&#x27;s next."/>
<meta name="twitter:image" content="/static/default-social-card.png"/>
<meta name="twitter:card" content="summary_large_image"/>
<meta name="twitter:site" content="@bluesky"/>
{%- endblock %}
<!-- TODO: link rel=canonical -->
<!-- TODO: analytics code -->
<!-- TODO: could put <link rel="preload"> tags here -->
<meta name="generator" name="bskyweb">
{% block head_metadata %}{% endblock %}
<link rel="apple-touch-icon" sizes="180x180" href="/static/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/static/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/static/favicon-16x16.png">
{% block html_head_extra -%}{%- endblock %}
<meta name="application-name" content="Bluesky">
<meta name="generator" content="bskyweb">
</head>
<body>
{%- block body_all %}
<div id="root"></div>
<noscript>
{%- block noscript_extra %}{% endblock -%}
<h1>Javascript Required</h1>
<p>This is a heavily interactive web application, and Javascript is required. Simple HTML interfaces are possible, but that is not what this is.
<h1>JavaScript Required</h1>
<p>This is a heavily interactive web application, and JavaScript is required. Simple HTML interfaces are possible, but that is not what this is.
<p>Learn more about Bluesky at <a href="https://blueskyweb.xyz">blueskyweb.xyz</a> and <a href="https://atproto.com">atproto.com</a>.
{% block noscript_extra %}{% endblock %}
</noscript>
{% endblock -%}
</body>
+10
View File
@@ -2,6 +2,16 @@
{% block head_title %}Bluesky{% endblock %}
{% block html_head_extra -%}
<meta name="description" content="See what's next.">
<meta property="og:type" content="website">
<meta property="og:title" content="Bluesky Social">
<meta property="og:description" content="See what's next.">
<meta property="og:image" content="/static/social-card-default.png">
<meta name="twitter:card" content="summary">
<meta name="twitter:site" content="@bluesky">
{%- endblock %}
{% block noscript_extra %}
<p>This is the home page.
{% endblock %}
+40 -17
View File
@@ -1,25 +1,48 @@
{% extends "base.html" %}
{% block head_page_meta -%}
<!-- TODO: "same as" indication with at:// URI? -->
{% block head_title %}
{%- if postView -%}
<meta property="og:type" content="article"/>
<meta name="twitter:card" content="summary"/>
{%- if postView.Author.DisplayName -%}
<meta property="og:title" content="{{ postView.Author.DisplayName }} / {{ postView.Author.Handle }}"/>
<meta name="twitter:title" content="{{ postView.Author.DisplayName }} / {{ postView.Author.Handle }}"/>
{%- else -%}
<meta property="og:title" content="{{ postView.Author.Handle }}"/>
<meta name="twitter:title" content="{{ postView.Author.Handle }}"/>
{%- endif -%}
{%- if postView.Record.Text -%}
<meta name="twitter:description" content="{{ postView.Record.Text }}"/>
<!-- TODO: could put any images in here, or author avatar -->
{%- endif -%}
@{{ postView.Author.Handle }} on Bluesky
{%- else -%}
Bluesky
{%- endif -%}
{% endblock %}
{% block html_head_extra -%}
{%- if postView -%}
<meta property="og:type" content="website">
<meta property="og:site_name" content="Bluesky Social">
{%- if requestURI %}
<meta property="og:url" content="{{ requestURI }}">
{% endif -%}
{%- if postView.Author.DisplayName %}
<meta property="og:title" content="{{ postView.Author.DisplayName }} (@{{ postView.Author.Handle }})">
{% else %}
<meta property="og:title" content="@{{ postView.Author.Handle }}">
{% endif -%}
{%- if postView.Record.Val.Text %}
<meta name="description" content="{{ postView.Record.Val.Text }}">
<meta property="og:description" content="{{ postView.Record.Val.Text }}">
{% endif -%}
{%- if imgThumbUrl %}
<meta property="og:image" content="{{ imgThumbUrl }}">
<meta name="twitter:card" content="summary_large_image">
{%- elif postView.Author.Avatar %}
{# Don't use avatar image in cards; usually looks bad #}
<meta name="twitter:card" content="summary">
{% endif %}
<meta name="twitter:label1" content="Posted At">
<meta name="twitter:value1" content="{{ postView.CreatedAt }}">
<meta name="twitter:site" content="@bluesky">
{% endif -%}
{%- endblock %}
{% block noscript_extra -%}
<p>{{ postView.Author.DisplayName }} / {{ postView.Author.Handle }}
<p>{{ postView.Record.Text }}
<div id="bsky_post_summary">
<h3>Post</h3>
<p id="bsky_display_name">{{ postView.Author.DisplayName }}</p>
<p id="bsky_handle">{{ postView.Author.Handle }}</p>
<p id="bsky_did">{{ postView.Author.Did }}</p>
<p id="bsky_post_text">{{ postView.Record.Val.Text }}</p>
</div>
{%- endblock %}
+40 -17
View File
@@ -1,25 +1,48 @@
{% extends "base.html" %}
{% block head_page_meta -%}
<!-- TODO: "same as" indication with DID? -->
{% block head_title %}
{%- if profileView -%}
<meta property="og:type" content="article"/>
<meta name="twitter:card" content="summary"/>
{%- if profileView.DisplayName -%}
<meta property="og:title" content="{{ profileView.DisplayName }} / {{ profileView.Handle }}"/>
<meta name="twitter:title" content="{{ profileView.DisplayName }} / {{ profileView.Handle }}"/>
{%- else -%}
<meta property="og:title" content="{{ profileView.Handle }}"/>
<meta name="twitter:title" content="{{ profileView.Handle }}"/>
{%- endif -%}
<meta name="twitter:description" content="{{ profileView.Description }}"/>
{%- if profileView.Avatar -%}
<meta name="twitter:image" content="{{ profileView.Avatar }}"/>
{%- endif -%}
@{{ profileView.Handle }} on Bluesky
{%- else -%}
Bluesky
{%- endif -%}
{% endblock %}
{% block html_head_extra -%}
{%- if profileView -%}
<meta property="og:type" content="website">
<meta property="og:site_name" content="Bluesky Social">
{%- if requestURI %}
<meta property="og:url" content="{{ requestURI }}">
{% endif -%}
{%- if profileView.DisplayName %}
<meta property="og:title" content="{{ profileView.DisplayName }} (@{{ profileView.Handle }})">
{% else %}
<meta property="og:title" content="{{ profileView.Handle }}">
{% endif -%}
{%- if profileView.Description %}
<meta name="description" content="{{ profileView.Description }}">
<meta property="og:description" content="{{ profileView.Description }}">
{% endif -%}
{%- if profileView.Banner %}
<meta property="og:image" content="{{ profileView.Banner }}">
<meta name="twitter:card" content="summary_large_image">
{%- elif profileView.Avatar -%}
{# Don't use avatar image in cards; usually looks bad #}
<meta name="twitter:card" content="summary">
{% endif %}
<meta name="twitter:label1" content="Account DID">
<meta name="twitter:value1" content="{{ profileView.Did }}">
<meta name="twitter:site" content="@bluesky">
{% endif -%}
{%- endblock %}
{% block noscript_extra -%}
<p>{{ profileView.DisplayName }} / {{ profileView.Handle }}
<p>{{ profileView.Description }}
<div id="bsky_profile_summary">
<h3>Profile</h3>
<p id="bsky_display_name">{{ profileView.DisplayName }}</p>
<p id="bsky_handle">{{ profileView.Handle }}</p>
<p id="bsky_did">{{ profileView.Did }}</p>
<p id="bsky_profile_description">{{ profileView.Description }}</p>
</div>
{%- endblock %}
+127
View File
@@ -0,0 +1,127 @@
# Build instructions
## App Build
- Set up your environment [using the react native instructions](https://reactnative.dev/docs/environment-setup).
- Setup your environment [for e2e testing using detox](https://wix.github.io/Detox/docs/introduction/getting-started):
- yarn global add detox-cli
- brew tap wix/brew
- brew install applesimutils
- After initial setup:
- Copy `google-services.json.example` to `google-services.json` or provide your own `google-services.json`. (A real firebase project is NOT required)
- `npx expo prebuild` -> you will also need to run this anytime `app.json` or native `package.json` deps change
- Start the dev servers
- `git clone git@github.com:bluesky-social/atproto.git`
- `cd atproto`
- `pnpm i`
- `pnpm build`
- `cd packages/dev-env && pnpm start`
- Run the dev app
- iOS: `yarn ios`
- Android: `yarn android`
- Web: `yarn web`
- If you are cloning or forking this repo as an open-source developer, please check the tips below as well
- Run e2e tests
- Start in various console tabs:
- `yarn e2e:mock-server`
- `yarn e2e:metro`
- Run once: `yarn e2e:build`
- Each test run: `yarn e2e:run`
- Tips
- Copy the `.env.example` to `.env` and fill in any necessary tokens. (The Sentry token is NOT required; see instructions below if you want to enable Sentry.)
- To run on the device, add `--device` to the command (e.g. `yarn android --device`). To build in production mode (slower build, faster app), also add `--variant release`.
- If you want to use Expo EAS on your own builds without ejecting from Expo, make sure to change the `owner` and `extra.eas.projectId` properties. If you do not have an Expo account, you may remove these properties.
- `npx react-native info` Checks what has been installed.
- If the Android simulator frequently hangs or is very sluggish, [bump its memory limit](https://stackoverflow.com/a/40068396)
- The Android simulator won't be able to access localhost services unless you run `adb reverse tcp:{PORT} tcp:{PORT}`
- For instance, the locally-hosted dev-wallet will need `adb reverse tcp:3001 tcp:3001`
- For some reason, the typescript compiler chokes on platform-specific files (e.g. `foo.native.ts`) but only when compiling for Web thus far. Therefore we always have one version of the file that doesn't use a platform specifier, and that should be the Web version. ([More info](https://stackoverflow.com/questions/44001050/platform-specific-import-component-in-react-native-with-typescript).)
### Adding Sentry
Adding Sentry is NOT required. You can keep `SENTRY_AUTH_TOKEN=` in `.env` which will build the app without Sentry.
However, if you're a part of the Bluesky team and want to enable Sentry, fill in `SENTRY_AUTH_TOKEN` in your `.env`. It can be created on the Sentry dashboard using [these instructions](https://docs.expo.dev/guides/using-sentry/#sign-up-for-a-sentry-account-and-create-a-project).
If you change `SENTRY_AUTH_TOKEN`, you need to do `yarn prebuild` before running `yarn ios` or `yarn android` again.
## Go-Server Build
### Prerequisites
- [Go](https://go.dev/)
- [Yarn](https://yarnpkg.com/)
### Steps
To run the build with Go, use staging credentials, your own, or any other account you create.
```
cd social-app
yarn && yarn build-web
cp ./web-build/static/js/*.* bskyweb/static/js/
cd bskyweb/
go mod tidy
go build -v -tags timetzdata -o bskyweb ./cmd/bskyweb
./bskyweb serve --pds-host=https://staging.bsky.dev --handle=<HANDLE> --password=<PASSWORD>
```
On build success, access the application at [http://localhost:8100/](http://localhost:8100/). Subsequent changes require re-running the above steps in order to be reflected.
## Various notes
### Debugging
- Note that since 0.70, debugging using the old debugger (which shows up using CMD+D) doesn't work anymore. Follow the instructions below to debug the code: https://reactnative.dev/docs/next/hermes#debugging-js-on-hermes-using-google-chromes-devtools
### Developer Menu
To open the [Developer Menu](https://docs.expo.dev/debugging/tools/#developer-menu) on an `expo-dev-client` app you can do the following:
- Android Device: Shake the device vertically, or if your device is connected via USB, run adb shell input keyevent 82 in your terminal
- Android Emulator: Either press Cmd ⌘ + m or Ctrl + m or run adb shell input keyevent 82 in your terminal
- iOS Device: Shake the device, or touch 3 fingers to the screen
- iOS Simulator: Press Ctrl + Cmd ⌘ + z on a Mac in the emulator to simulate the shake gesture or press Cmd ⌘ + d
### Running E2E Tests
- Make sure you've set your environment following the above
- Make sure Metro and the dev server are running
- Run `yarn e2e`
- Find the artifacts in the `artifact` folder
### Polyfills
`./platform/polyfills.*.ts` adds polyfills to the environment. Currently, this includes:
- TextEncoder / TextDecoder
### Sentry sourcemaps
Sourcemaps should automatically be updated when a signed build is created using `eas build` and published using `eas submit` due to the postPublish hook setup in `app.json`. However, if an update is created and published OTA using `eas update`, we need to take the following steps to upload sourcemaps to Sentry:
- Run eas update. This will generate a dist folder in your project root, which contains your JavaScript bundles and source maps. This command will also output the 'Android update ID' and 'iOS update ID' that we'll need in the next step.
- Copy or rename the bundle names in the `dist/bundles` folder to match `index.android.bundle` (Android) or `main.jsbundle` (iOS).
- Next, you can use the Sentry CLI to upload your bundles and source maps:
- release name should be set to `${bundleIdentifier}@${version}+${buildNumber}` (iOS) or `${androidPackage}@${version}+${versionCode}` (Android), so for example `com.domain.myapp@1.0.0+1`.
- `dist` should be set to the Update ID that `eas update` generated.
- Command for Android:
`node_modules/@sentry/cli/bin/sentry-cli releases \
files <release name> \
upload-sourcemaps \
--dist <Android Update ID> \
--rewrite \
dist/bundles/index.android.bundle dist/bundles/android-<hash>.map`
- Command for iOS:
`node_modules/@sentry/cli/bin/sentry-cli releases \
files <release name> \
upload-sourcemaps \
--dist <iOS Update ID> \
--rewrite \
dist/bundles/main.jsbundle dist/bundles/ios-<hash>.map`
### OTA updates
To create OTA updates, run `eas update` along with the `--branch` flag to indicate which branch you want to push the update to, and the `--message` flag to indicate a message for yourself and your team that shows up on https://expo.dev. All the channels (which make up the options for the `--branch` flag) are given in `eas.json`. [See more here](https://docs.expo.dev/eas-update/getting-started/)
The clients which can receive an OTA update are governed by the `runtimeVersion` property in `app.json`. Right now, it is set so that only apps with the same `appVersion` (same as `version` property in `app.json`) can receive the update and install it. However, we can manually set `"runtimeVersion": "1.34.0"` or anything along those lines as well. This is useful if very little native code changes from update to update. If we are manually setting `runtimeVersion`, we should increment the version each time the native code is changed. [See more here](https://docs.expo.dev/eas-update/runtime-versions/)
+113
View File
@@ -0,0 +1,113 @@
# Internationalization
We want the official Bluesky app to be supported in as many languages as possible. If you want to help us translate the app, please open a PR or issue on the [Bluesky app repo on GitHub](https://github.com/bluesky-social/social-app)
## Tools
We are using Lingui to manage translations. You can find the documentation [here](https://lingui.dev/).
### Adding new strings
When adding a new string, do it as follows:
```jsx
// Before
import { Text } from "react-native";
<Text>Hello World</Text>
```
```jsx
// After
import { Text } from "react-native";
import { Trans } from "@lingui/macro";
<Text><Trans>Hello World</Trans></Text>
```
The `<Trans>` macro will extract the string and add it to the catalog. It is not really a component, but a macro. Further reading [here](https://lingui.dev/ref/macro.html)
However sometimes you will run into this case:
```jsx
// Before
import { Text } from "react-native";
const text = "Hello World";
<Text accessibilityLabel="Label is here">{text}</Text>
```
In this case, you cannot use the `useLingui()` hook:
```jsx
import { msg } from "@lingui/macro";
import { useLingui } from "@lingui/react";
const { _ } = useLingui();
return <Text accessibilityLabel={_(msg`Label is here`)}>{text}</Text>
```
If you want to do this outside of a React component, you can use the `t` macro instead (note: this won't react to changes if the locale is switched dynamically within the app):
```jsx
import { t } from "@lingui/macro";
const text = t`Hello World`;
```
We can then run `yarn intl:extract` to update the catalog in `src/locale/locales/{locale}/messages.po`. This will add the new string to the catalog.
We can then run `yarn intl:compile` to update the translation files in `src/locale/locales/{locale}/messages.js`. This will add the new string to the translation files.
The configuration for translations is defined in `lingui.config.js`
So the workflow is as follows:
1. Wrap messages in Trans macro
2. Run `yarn intl:extract` command to generate message catalogs
3. Translate message catalogs (send them to translators usually)
4. Run `yarn intl:compile` to create runtime catalogs
5. Load runtime catalog
6. Enjoy translated app!
### Common pitfalls
These pitfalls are memoization pitfalls that will cause the components to not re-render when the locale is changed -- causing stale translations to be shown.
```jsx
import { msg } from "@lingui/macro";
import { i18n } from "@lingui/core";
const welcomeMessage = msg`Welcome!`;
// ❌ Bad! This code won't work
export function Welcome() {
const buggyWelcome = useMemo(() => {
return i18n._(welcomeMessage);
}, []);
return <div>{buggyWelcome}</div>;
}
// ❌ Bad! This code won't work either because the reference to i18n does not change
export function Welcome() {
const { i18n } = useLingui();
const buggyWelcome = useMemo(() => {
return i18n._(welcomeMessage);
}, [i18n]);
return <div>{buggyWelcome}</div>;
}
// ✅ Good! `useMemo` has i18n context in the dependency
export function Welcome() {
const linguiCtx = useLingui();
const welcome = useMemo(() => {
return linguiCtx.i18n._(welcomeMessage);
}, [linguiCtx]);
return <div>{welcome}</div>;
}
// 🤩 Better! `useMemo` consumes the `_` function from the Lingui context
export function Welcome() {
const { _ } = useLingui();
const welcome = useMemo(() => {
return _(welcomeMessage);
}, [_]);
return <div>{welcome}</div>;
}
```
+14
View File
@@ -0,0 +1,14 @@
# Testing instructions
### Using Maestro E2E tests
1. Install Maestro by following [these instructions](https://maestro.mobile.dev/getting-started/installing-maestro). This will help us run the E2E tests.
2. You can write Maestro tests in `__e2e__/maestro` directory by creating a new `.yaml` file or by modifying an existing one.
3. You can also use [Maestro Studio](https://maestro.mobile.dev/getting-started/maestro-studio) which automatically generates commands by recording your actions on the app. Therefore, you can create realistic tests without having to manually write any code. Use the `maestro studio` command to start recording your actions.
### Using Flashlight for Performance Testing
1. Make sure Maestro is installed (optional: only for automated testing) by following the instructions above
2. Install Flashlight by following [these instructions](https://docs.flashlight.dev/)
3. The simplest way to get started is by running `yarn perf:measure` which will run a live preview of the performance test results. You can [see a demo here](https://github.com/bamlab/flashlight/assets/4534323/4038a342-f145-4c3b-8cde-17949bf52612)
4. The `yarn perf:test:measure` will run the `scroll.yaml` test located in `__e2e__/maestro/scroll.yaml` and give the results in `.perf/results.json` which can be viewed by running `yarn:perf:results`
5. You can also run your own tests by running `yarn perf:test <path_to_test>` where `<path_to_test>` is the path to your test file. For example, `yarn perf:test __e2e__/maestro/scroll.yaml` will run the `scroll.yaml` test located in `__e2e__/maestro/scroll.yaml`.
+21 -4
View File
@@ -1,28 +1,45 @@
{
"cli": {
"version": ">= 3.8.1"
"version": ">= 3.8.1",
"promptToConfigurePushNotifications": false
},
"build": {
"development": {
"developmentClient": true,
"distribution": "internal",
"ios": {
"resourceClass": "m-medium"
"simulator": true,
"resourceClass": "m-large"
},
"channel": "development"
},
"development-device": {
"developmentClient": true,
"distribution": "internal",
"ios": {
"resourceClass": "m-large"
},
"channel": "development"
},
"preview": {
"distribution": "internal",
"ios": {
"resourceClass": "m-medium"
"resourceClass": "m-large"
},
"channel": "preview"
},
"production": {
"ios": {
"resourceClass": "m-medium"
"resourceClass": "m-large"
},
"channel": "production"
},
"dev-android-apk": {
"developmentClient": true,
"android": {
"buildType": "apk",
"gradleCommand": ":app:assembleRelease"
}
}
},
"submit": {
+41
View File
@@ -0,0 +1,41 @@
{
"project_info": {
"project_id": "blueskyweb-example",
"project_number": "100000000000",
"firebase_url": "https://blueskyweb-example.firebaseio.com"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:123456789000:android:f1bf012572b04063",
"android_client_info": {
"package_name": "xyz.blueskyweb.app"
}
},
"oauth_client": [
{
"client_id": "123456789000.apps.googleusercontent.com",
"client_type": 3
}
],
"api_key": [
{
"current_key": "123456789000"
}
],
"services": {
"analytics_service": {
"status": 1
},
"appinvite_service": {
"status": 1,
"other_platform_oauth_client": []
},
"ads_service": {
"status": 2
}
}
}
],
"configuration_version": "1"
}
+92
View File
@@ -0,0 +1,92 @@
#!/usr/bin/env sh
get_container_id() {
local compose_file=$1
local service=$2
if [ -z "${compose_file}" ] || [ -z "${service}" ]; then
echo "usage: get_container_id <compose_file> <service>"
exit 1
fi
docker compose -f $compose_file ps --format json --status running \
| jq -r '.[]? | select(.Service == "'${service}'") | .ID'
}
# Exports all environment variables
export_env() {
export_pg_env
export_redis_env
}
# Exports postgres environment variables
export_pg_env() {
# Based on creds in compose.yaml
export PGPORT=5433
export PGHOST=localhost
export PGUSER=pg
export PGPASSWORD=password
export PGDATABASE=postgres
export DB_POSTGRES_URL="postgresql://pg:password@127.0.0.1:5433/postgres"
}
# Exports redis environment variables
export_redis_env() {
export REDIS_HOST="127.0.0.1:6380"
}
# Main entry point
main() {
# Expect a SERVICES env var to be set with the docker service names
local services=${SERVICES}
dir=$(dirname $0)
compose_file="${dir}/docker-compose.yaml"
# whether this particular script started the container(s)
started_container=false
# trap SIGINT and performs cleanup as necessary, i.e.
# taking down containers if this script started them
trap "on_sigint ${services}" INT
on_sigint() {
local services=$@
echo # newline
if $started_container; then
docker compose -f $compose_file rm -f --stop --volumes ${services}
fi
exit $?
}
# check if all services are running already
not_running=false
for service in $services; do
container_id=$(get_container_id $compose_file $service)
if [ -z $container_id ]; then
not_running=true
break
fi
done
# if any are missing, recreate all services
if $not_running; then
docker compose -f $compose_file up --wait --force-recreate ${services}
started_container=true
else
echo "all services ${services} are already running"
fi
# setup environment variables and run args
export_env
"$@"
# save return code for later
code=$?
# performs cleanup as necessary, i.e. taking down containers
# if this script started them
echo # newline
if $started_container; then
docker compose -f $compose_file rm -f --stop --volumes ${services}
fi
exit ${code}
}
+49
View File
@@ -0,0 +1,49 @@
version: '3.8'
services:
# An ephermerally-stored postgres database for single-use test runs
db_test: &db_test
image: postgres:14.4-alpine
environment:
- POSTGRES_USER=pg
- POSTGRES_PASSWORD=password
ports:
- '5433:5432'
# Healthcheck ensures db is queryable when `docker-compose up --wait` completes
healthcheck:
test: 'pg_isready -U pg'
interval: 500ms
timeout: 10s
retries: 20
# A persistently-stored postgres database
db:
<<: *db_test
ports:
- '5432:5432'
healthcheck:
disable: true
volumes:
- atp_db:/var/lib/postgresql/data
# An ephermerally-stored redis cache for single-use test runs
redis_test: &redis_test
image: redis:7.0-alpine
ports:
- '6380:6379'
# Healthcheck ensures redis is queryable when `docker-compose up --wait` completes
healthcheck:
test: ['CMD-SHELL', '[ "$$(redis-cli ping)" = "PONG" ]']
interval: 500ms
timeout: 10s
retries: 20
# A persistently-stored redis cache
redis:
<<: *redis_test
command: redis-server --save 60 1 --loglevel warning
ports:
- '6379:6379'
healthcheck:
disable: true
volumes:
- atp_redis:/data
volumes:
atp_db:
atp_redis:
+9
View File
@@ -0,0 +1,9 @@
#!/usr/bin/env sh
# Example usage:
# ./with-test-db.sh psql postgresql://pg:password@localhost:5433/postgres -c 'select 1;'
dir=$(dirname $0)
. ${dir}/_common.sh
SERVICES="db_test" main "$@"
+10
View File
@@ -0,0 +1,10 @@
#!/usr/bin/env sh
# Example usage:
# ./with-test-redis-and-db.sh psql postgresql://pg:password@localhost:5433/postgres -c 'select 1;'
# ./with-test-redis-and-db.sh redis-cli -h localhost -p 6380 ping
dir=$(dirname $0)
. ${dir}/_common.sh
SERVICES="db_test redis_test" main "$@"
+11
View File
@@ -74,3 +74,14 @@ jest.mock('lande', () => ({
__esModule: true, // this property makes it work
default: jest.fn().mockReturnValue([['eng']]),
}))
jest.mock('sentry-expo', () => ({
init: () => jest.fn(),
Native: {
ReactNativeTracing: jest.fn().mockImplementation(() => ({
start: jest.fn(),
stop: jest.fn(),
})),
ReactNavigationInstrumentation: jest.fn(),
},
}))
+303 -90
View File
@@ -1,17 +1,8 @@
import {AddressInfo} from 'net'
import os from 'os'
import net from 'net'
import path from 'path'
import * as crypto from '@atproto/crypto'
import {PDS, ServerConfig, Database, MemoryBlobStore} from '@atproto/pds'
import * as plc from '@did-plc/lib'
import {PlcServer, Database as PlcDatabase} from '@did-plc/server'
import {BskyAgent} from '@atproto/api'
const ADMIN_PASSWORD = 'admin-pass'
const SECOND = 1000
const MINUTE = SECOND * 60
const HOUR = MINUTE * 60
import fs from 'fs'
import {TestNetwork} from '@atproto/dev-env'
import {AtUri, BskyAgent} from '@atproto/api'
export interface TestUser {
email: string
@@ -27,89 +18,72 @@ export interface TestPDS {
close: () => Promise<void>
}
class StringIdGenerator {
_nextId = [0]
constructor(
public _chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',
) {}
next() {
const r = []
for (const char of this._nextId) {
r.unshift(this._chars[char])
}
this._increment()
return r.join('')
}
_increment() {
for (let i = 0; i < this._nextId.length; i++) {
const val = ++this._nextId[i]
if (val >= this._chars.length) {
this._nextId[i] = 0
} else {
return
}
}
this._nextId.push(0)
}
*[Symbol.iterator]() {
while (true) {
yield this.next()
}
}
}
const ids = new StringIdGenerator()
export async function createServer(
{inviteRequired}: {inviteRequired: boolean} = {inviteRequired: false},
): Promise<TestPDS> {
const repoSigningKey = await crypto.Secp256k1Keypair.create()
const plcRotationKey = await crypto.Secp256k1Keypair.create()
const port = await getPort()
const plcDb = PlcDatabase.mock()
const plcServer = PlcServer.create({db: plcDb})
const plcListener = await plcServer.start()
const plcPort = (plcListener.address() as AddressInfo).port
const plcUrl = `http://localhost:${plcPort}`
const recoveryKey = (await crypto.Secp256k1Keypair.create()).did()
const plcClient = new plc.Client(plcUrl)
const serverDid = await plcClient.createDid({
signingKey: repoSigningKey.did(),
rotationKeys: [recoveryKey, plcRotationKey.did()],
handle: 'localhost',
pds: `http://localhost:${port}`,
signer: plcRotationKey,
})
const blobstoreLoc = path.join(os.tmpdir(), crypto.randomStr(5, 'base32'))
const cfg = new ServerConfig({
debugMode: true,
version: '0.0.0',
scheme: 'http',
hostname: 'localhost',
port,
serverDid,
recoveryKey,
adminPassword: ADMIN_PASSWORD,
inviteRequired,
didPlcUrl: plcUrl,
jwtSecret: 'jwt-secret',
availableUserDomains: ['.test'],
appUrlPasswordReset: 'app://forgot-password',
emailNoReplyAddress: 'noreply@blueskyweb.xyz',
publicUrl: `http://localhost:${port}`,
imgUriSalt: '9dd04221f5755bce5f55f47464c27e1e',
imgUriKey:
'f23ecd142835025f42c3db2cf25dd813956c178392760256211f9d315f8ab4d8',
dbPostgresUrl: process.env.DB_POSTGRES_URL,
blobstoreLocation: `${blobstoreLoc}/blobs`,
blobstoreTmp: `${blobstoreLoc}/tmp`,
maxSubscriptionBuffer: 200,
repoBackfillLimitMs: HOUR,
userInviteInterval: 1,
labelerDid: 'did:example:labeler',
labelerKeywords: {},
})
const db =
cfg.dbPostgresUrl !== undefined
? Database.postgres({
url: cfg.dbPostgresUrl,
schema: cfg.dbPostgresSchema,
})
: Database.memory()
await db.migrateToLatestOrThrow()
const blobstore = new MemoryBlobStore()
const pds = PDS.create({
db,
blobstore,
repoSigningKey,
plcRotationKey,
config: cfg,
})
await pds.start()
const port2 = await getPort(port + 1)
const pdsUrl = `http://localhost:${port}`
const id = ids.next()
const testNet = await TestNetwork.create({
pds: {
port,
publicUrl: pdsUrl,
inviteRequired,
dbPostgresSchema: `pds_${id}`,
},
bsky: {
dbPostgresSchema: `bsky_${id}`,
},
plc: {port: port2},
})
const pic = fs.readFileSync(
path.join(__dirname, '..', 'assets', 'default-avatar.jpg'),
)
return {
pdsUrl,
mocker: new Mocker(pdsUrl),
mocker: new Mocker(testNet, pdsUrl, pic),
async close() {
await pds.destroy()
await plcServer.destroy()
await testNet.pds.server.destroy()
await testNet.plc.server.destroy()
},
}
}
@@ -118,10 +92,26 @@ class Mocker {
agent: BskyAgent
users: Record<string, TestUser> = {}
constructor(public service: string) {
constructor(
public testNet: TestNetwork,
public service: string,
public pic: Uint8Array,
) {
this.agent = new BskyAgent({service})
}
get pds() {
return this.testNet.pds
}
get bsky() {
return this.testNet.bsky
}
get plc() {
return this.testNet.plc
}
// NOTE
// deterministic date generator
// we use this to ensure the mock dataset is always the same
@@ -140,7 +130,7 @@ class Mocker {
const inviteRes = await agent.api.com.atproto.server.createInviteCode(
{useCount: 1},
{
headers: {authorization: `Basic ${btoa(`admin:${ADMIN_PASSWORD}`)}`},
headers: this.pds.adminAuthHeaders('admin'),
encoding: 'application/json',
},
)
@@ -152,6 +142,15 @@ class Mocker {
handle: name + '.test',
password: 'hunter2',
})
await agent.upsertProfile(async () => {
const blob = await agent.uploadBlob(this.pic, {
encoding: 'image/jpeg',
})
return {
displayName: name,
avatar: blob.data.blob,
}
})
this.users[name] = {
did: res.data.did,
email,
@@ -192,6 +191,220 @@ class Mocker {
await this.follow('carla', 'alice')
await this.follow('carla', 'bob')
}
async createPost(user: string, text: string) {
const agent = this.users[user]?.agent
if (!agent) {
throw new Error(`Not a user: ${user}`)
}
return await agent.post({
text,
langs: ['en'],
createdAt: new Date().toISOString(),
})
}
async createImagePost(user: string, text: string) {
const agent = this.users[user]?.agent
if (!agent) {
throw new Error(`Not a user: ${user}`)
}
const blob = await agent.uploadBlob(this.pic, {
encoding: 'image/jpeg',
})
return await agent.post({
text,
langs: ['en'],
embed: {
$type: 'app.bsky.embed.images',
images: [{image: blob.data.blob, alt: ''}],
},
createdAt: new Date().toISOString(),
})
}
async createQuotePost(
user: string,
text: string,
{uri, cid}: {uri: string; cid: string},
) {
const agent = this.users[user]?.agent
if (!agent) {
throw new Error(`Not a user: ${user}`)
}
return await agent.post({
text,
embed: {$type: 'app.bsky.embed.record', record: {uri, cid}},
langs: ['en'],
createdAt: new Date().toISOString(),
})
}
async createReply(
user: string,
text: string,
{uri, cid}: {uri: string; cid: string},
) {
const agent = this.users[user]?.agent
if (!agent) {
throw new Error(`Not a user: ${user}`)
}
return await agent.post({
text,
reply: {root: {uri, cid}, parent: {uri, cid}},
langs: ['en'],
createdAt: new Date().toISOString(),
})
}
async like(user: string, {uri, cid}: {uri: string; cid: string}) {
const agent = this.users[user]?.agent
if (!agent) {
throw new Error(`Not a user: ${user}`)
}
return await agent.like(uri, cid)
}
async createFeed(user: string, rkey: string, posts: string[]) {
const agent = this.users[user]?.agent
if (!agent) {
throw new Error(`Not a user: ${user}`)
}
const fgUri = AtUri.make(
this.users[user].did,
'app.bsky.feed.generator',
rkey,
)
const fg1 = await this.testNet.createFeedGen({
[fgUri.toString()]: async () => {
return {
encoding: 'application/json',
body: {
feed: posts.slice(0, 30).map(uri => ({post: uri})),
},
}
},
})
const avatarRes = await agent.api.com.atproto.repo.uploadBlob(this.pic, {
encoding: 'image/png',
})
return await agent.api.app.bsky.feed.generator.create(
{repo: this.users[user].did, rkey},
{
did: fg1.did,
displayName: rkey,
description: 'all my fav stuff',
avatar: avatarRes.data.blob,
createdAt: new Date().toISOString(),
},
)
}
async createInvite(forAccount: string) {
const agent = new BskyAgent({service: this.agent.service})
await agent.api.com.atproto.server.createInviteCode(
{useCount: 1, forAccount},
{
headers: this.pds.adminAuthHeaders('admin'),
encoding: 'application/json',
},
)
}
async labelAccount(label: string, user: string) {
const did = this.users[user]?.did
if (!did) {
throw new Error(`Invalid user: ${user}`)
}
const ctx = this.bsky.ctx
if (!ctx) {
throw new Error('Invalid appview')
}
const labelSrvc = ctx.services.label(ctx.db.getPrimary())
await labelSrvc.createLabels([
{
src: ctx.cfg.labelerDid,
uri: did,
cid: '',
val: label,
neg: false,
cts: new Date().toISOString(),
},
])
}
async labelProfile(label: string, user: string) {
const agent = this.users[user]?.agent
const did = this.users[user]?.did
if (!did) {
throw new Error(`Invalid user: ${user}`)
}
const profile = await agent.app.bsky.actor.profile.get({
repo: user + '.test',
rkey: 'self',
})
const ctx = this.bsky.ctx
if (!ctx) {
throw new Error('Invalid appview')
}
const labelSrvc = ctx.services.label(ctx.db.getPrimary())
await labelSrvc.createLabels([
{
src: ctx.cfg.labelerDid,
uri: profile.uri,
cid: profile.cid,
val: label,
neg: false,
cts: new Date().toISOString(),
},
])
}
async labelPost(label: string, {uri, cid}: {uri: string; cid: string}) {
const ctx = this.bsky.ctx
if (!ctx) {
throw new Error('Invalid appview')
}
const labelSrvc = ctx.services.label(ctx.db.getPrimary())
await labelSrvc.createLabels([
{
src: ctx.cfg.labelerDid,
uri,
cid,
val: label,
neg: false,
cts: new Date().toISOString(),
},
])
}
async createMuteList(user: string, name: string): Promise<string> {
const res = await this.users[user]?.agent.app.bsky.graph.list.create(
{repo: this.users[user]?.did},
{
purpose: 'app.bsky.graph.defs#modlist',
name,
createdAt: new Date().toISOString(),
},
)
await this.users[user]?.agent.app.bsky.graph.muteActorList({
list: res.uri,
})
return res.uri
}
async addToMuteList(owner: string, list: string, subject: string) {
await this.users[owner]?.agent.app.bsky.graph.listitem.create(
{repo: this.users[owner]?.did},
{
list,
subject,
createdAt: new Date().toISOString(),
},
)
}
}
const checkAvailablePort = (port: number) =>
@@ -206,8 +419,8 @@ const checkAvailablePort = (port: number) =>
})
})
async function getPort() {
for (let i = 3000; i < 65000; i++) {
async function getPort(start = 3000) {
for (let i = start; i < 65000; i++) {
if (await checkAvailablePort(i)) {
return i
}
+11
View File
@@ -0,0 +1,11 @@
/** @type {import('@lingui/conf').LinguiConfig} */
module.exports = {
locales: ['en', 'cs', 'fr', 'hi', 'es'],
catalogs: [
{
path: '<rootDir>/src/locale/locales/{locale}/messages',
include: ['src'],
},
],
format: 'po',
}
+18
View File
@@ -1,7 +1,25 @@
// Learn more https://docs.expo.io/guides/customizing-metro
const {getDefaultConfig} = require('expo/metro-config')
const cfg = getDefaultConfig(__dirname)
cfg.resolver.sourceExts = process.env.RN_SRC_EXT
? process.env.RN_SRC_EXT.split(',').concat(cfg.resolver.sourceExts)
: cfg.resolver.sourceExts
cfg.transformer.getTransformOptions = async () => ({
transform: {
experimentalImportSupport: true,
inlineRequires: true,
nonInlinedRequires: [
// We can remove this option and rely on the default after
// https://github.com/facebook/metro/pull/1126 is released.
'React',
'react',
'react/jsx-dev-runtime',
'react/jsx-runtime',
'react-native',
],
},
})
module.exports = cfg
+111 -48
View File
@@ -1,41 +1,59 @@
{
"name": "bsky.app",
"version": "1.15.0",
"version": "1.56.0",
"private": true,
"scripts": {
"prepare": "is-ci || husky install",
"postinstall": "patch-package",
"prebuild": "expo prebuild --clean",
"android": "expo run:android",
"ios": "expo run:ios",
"web": "expo start --web",
"build-web": "expo export:web && node ./scripts/post-web-build.js",
"build-web": "expo export:web && node ./scripts/post-web-build.js && cp --verbose ./web-build/static/js/*.* ./bskyweb/static/js/",
"start": "expo start --dev-client",
"start:prod": "expo start --dev-client --no-dev --minify",
"clean-cache": "rm -rf node_modules/.cache/babel-loader/*",
"test": "jest --forceExit --testTimeout=20000 --bail",
"test-watch": "jest --watchAll",
"test-ci": "jest --ci --forceExit --reporters=default --reporters=jest-junit",
"test-coverage": "jest --coverage",
"test": "NODE_ENV=test jest --forceExit --testTimeout=20000 --bail",
"test-watch": "NODE_ENV=test jest --watchAll",
"test-ci": "NODE_ENV=test jest --ci --forceExit --reporters=default --reporters=jest-junit",
"test-coverage": "NODE_ENV=test jest --coverage",
"lint": "eslint ./src --ext .js,.jsx,.ts,.tsx",
"e2e:mock-server": "ts-node __e2e__/mock-server.ts",
"typecheck": "tsc --project ./tsconfig.check.json",
"e2e:mock-server": "./jest/dev-infra/with-test-redis-and-db.sh ts-node __e2e__/mock-server.ts",
"e2e:metro": "RN_SRC_EXT=e2e.ts,e2e.tsx expo run:ios",
"e2e:build": "detox build -c ios.sim.debug",
"e2e:run": "detox test --configuration ios.sim.debug --take-screenshots all"
"e2e:run": "detox test --configuration ios.sim.debug --take-screenshots all",
"perf:test": "NODE_ENV=test maestro test",
"perf:test:run": "NODE_ENV=test maestro test __e2e__/maestro/scroll.yaml",
"perf:test:measure": "NODE_ENV=test flashlight test --bundleId xyz.blueskyweb.app --testCommand 'yarn perf:test' --duration 150000 --resultsFilePath .perf/results.json",
"perf:test:results": "NODE_ENV=test flashlight report .perf/results.json",
"perf:measure": "NODE_ENV=test flashlight measure",
"build:apk": "eas build -p android --profile dev-android-apk",
"intl:extract": "lingui extract",
"intl:compile": "lingui compile"
},
"dependencies": {
"@atproto/api": "0.2.6",
"@atproto/api": "^0.6.23",
"@bam.tech/react-native-image-resizer": "^3.0.4",
"@expo/webpack-config": "^18.0.1",
"@braintree/sanitize-url": "^6.0.2",
"@emoji-mart/react": "^1.1.1",
"@expo/html-elements": "^0.4.2",
"@expo/webpack-config": "^19.0.0",
"@fortawesome/fontawesome-svg-core": "^6.1.1",
"@fortawesome/free-regular-svg-icons": "^6.1.1",
"@fortawesome/free-solid-svg-icons": "^6.1.1",
"@fortawesome/react-native-fontawesome": "^0.3.0",
"@gorhom/bottom-sheet": "^4",
"@mattermost/react-native-paste-input": "^0.6.0",
"@miblanchard/react-native-slider": "^2.2.0",
"@notifee/react-native": "^7.4.0",
"@react-native-async-storage/async-storage": "^1.17.6",
"@gorhom/bottom-sheet": "^4.5.1",
"@lingui/react": "^4.5.0",
"@mattermost/react-native-paste-input": "^0.6.4",
"@miblanchard/react-native-slider": "^2.3.1",
"@react-native-async-storage/async-storage": "1.18.2",
"@react-native-camera-roll/camera-roll": "^5.2.2",
"@react-native-clipboard/clipboard": "^1.10.0",
"@react-native-community/blur": "^4.3.0",
"@react-native-community/datetimepicker": "7.2.0",
"@react-native-menu/menu": "^0.8.0",
"@react-native-picker/picker": "2.4.10",
"@react-navigation/bottom-tabs": "^6.5.7",
"@react-navigation/drawer": "^6.6.2",
"@react-navigation/native": "^6.1.6",
@@ -44,90 +62,118 @@
"@segment/analytics-react": "^1.0.0-rc1",
"@segment/analytics-react-native": "^2.10.1",
"@segment/sovran-react-native": "^0.4.5",
"@sentry/react-native": "5.10.0",
"@tanstack/react-query": "^5.8.1",
"@tiptap/core": "^2.0.0-beta.220",
"@tiptap/extension-document": "^2.0.0-beta.220",
"@tiptap/extension-link": "^2.0.0-beta.220",
"@tiptap/extension-hard-break": "^2.0.3",
"@tiptap/extension-history": "^2.0.3",
"@tiptap/extension-mention": "^2.0.0-beta.220",
"@tiptap/extension-paragraph": "^2.0.0-beta.220",
"@tiptap/extension-placeholder": "^2.0.0-beta.220",
"@tiptap/extension-text": "^2.0.0-beta.220",
"@tiptap/html": "^2.1.11",
"@tiptap/pm": "^2.0.0-beta.220",
"@tiptap/react": "^2.0.0-beta.220",
"@tiptap/suggestion": "^2.0.0-beta.220",
"@types/node": "^18.16.2",
"@zxing/text-encoding": "^0.9.0",
"array.prototype.findlast": "^1.2.3",
"await-lock": "^2.2.2",
"babel-plugin-transform-remove-console": "^6.9.4",
"base64-js": "^1.5.1",
"bcp-47-match": "^2.0.3",
"date-fns": "^2.30.0",
"email-validator": "^2.0.4",
"expo": "~48.0.10",
"expo-build-properties": "~0.5.1",
"expo-camera": "~13.2.1",
"expo-dev-client": "~2.1.1",
"expo-image": "~1.0.0",
"expo-image-picker": "~14.1.1",
"expo-localization": "~14.1.1",
"expo-media-library": "~15.2.3",
"expo-splash-screen": "~0.18.1",
"expo-status-bar": "~1.4.4",
"expo-system-ui": "~2.2.1",
"expo-updates": "~0.16.4",
"emoji-mart": "^5.5.2",
"eventemitter3": "^5.0.1",
"expo": "^49.0.8",
"expo-application": "~5.3.0",
"expo-build-properties": "~0.8.3",
"expo-camera": "13.5.1",
"expo-constants": "~14.4.2",
"expo-dev-client": "2.4.7",
"expo-device": "~5.4.0",
"expo-image": "~1.3.2",
"expo-image-manipulator": "~11.5.0",
"expo-image-picker": "~14.5.0",
"expo-localization": "~14.3.0",
"expo-media-library": "~15.4.1",
"expo-notifications": "~0.20.1",
"expo-sharing": "~11.5.0",
"expo-splash-screen": "~0.20.5",
"expo-status-bar": "~1.6.0",
"expo-system-ui": "~2.4.0",
"expo-updates": "~0.18.12",
"fast-text-encoding": "^1.0.6",
"graphemer": "^1.4.0",
"he": "^1.2.0",
"history": "^5.3.0",
"js-sha256": "^0.9.0",
"lande": "^1.0.10",
"lodash.chunk": "^4.2.0",
"lodash.clonedeep": "^4.5.0",
"lodash.debounce": "^4.0.8",
"lodash.isequal": "^4.5.0",
"lodash.omit": "^4.5.0",
"lodash.once": "^4.1.1",
"lodash.random": "^3.2.0",
"lodash.samplesize": "^4.2.0",
"lodash.set": "^4.3.2",
"lodash.shuffle": "^4.2.0",
"lru_map": "^0.4.1",
"mobx": "^6.6.1",
"mobx-react-lite": "^3.4.0",
"mobx-utils": "^6.0.6",
"nanoid": "^5.0.2",
"normalize-url": "^8.0.0",
"patch-package": "^6.5.1",
"postinstall-postinstall": "^2.1.0",
"psl": "^1.9.0",
"react": "18.2.0",
"react-avatar-editor": "^13.0.0",
"react-circular-progressbar": "^2.1.0",
"react-dom": "^18.2.0",
"react-native": "0.71.6",
"react-native": "0.72.5",
"react-native-appstate-hook": "^1.0.6",
"react-native-background-fetch": "^4.1.8",
"react-native-drawer-layout": "^3.2.0",
"react-native-fs": "^2.20.0",
"react-native-gesture-handler": "~2.9.0",
"react-native-gesture-handler": "^2.12.1",
"react-native-get-random-values": "^1.8.0",
"react-native-haptic-feedback": "^1.14.0",
"react-native-image-crop-picker": "^0.38.1",
"react-native-inappbrowser-reborn": "^3.6.3",
"react-native-ios-context-menu": "^1.15.3",
"react-native-linear-gradient": "^2.6.2",
"react-native-pager-view": "6.1.4",
"react-native-picker-select": "^8.1.0",
"react-native-progress": "bluesky-social/react-native-progress",
"react-native-reanimated": "~2.14.4",
"react-native-reanimated": "^3.4.2",
"react-native-root-siblings": "^4.1.1",
"react-native-safe-area-context": "^4.4.1",
"react-native-screens": "^3.13.1",
"react-native-safe-area-context": "4.6.3",
"react-native-screens": "~3.22.0",
"react-native-splash-screen": "^3.3.0",
"react-native-svg": "13.4.0",
"react-native-svg": "13.9.0",
"react-native-url-polyfill": "^1.3.0",
"react-native-uuid": "^2.0.1",
"react-native-version-number": "^0.3.6",
"react-native-web": "^0.18.11",
"react-native-web": "~0.19.6",
"react-native-web-linear-gradient": "^1.1.2",
"react-responsive": "^9.0.2",
"rn-fetch-blob": "^0.12.0",
"sentry-expo": "~7.0.1",
"tippy.js": "^6.3.7",
"tlds": "^1.234.0",
"use-deep-compare": "^1.1.0",
"zeego": "^1.6.2",
"zod": "^3.20.2"
},
"devDependencies": {
"@atproto/pds": "^0.1.4",
"@babel/core": "^7.20.0",
"@atproto/dev-env": "^0.2.5",
"@babel/core": "^7.23.2",
"@babel/preset-env": "^7.20.0",
"@babel/runtime": "^7.20.0",
"@did-plc/server": "^0.0.1",
"@lingui/cli": "^4.5.0",
"@lingui/macro": "^4.5.0",
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.11",
"@react-native-community/eslint-config": "^3.0.0",
"@testing-library/jest-native": "^5.4.1",
"@testing-library/react-native": "^11.5.2",
@@ -135,43 +181,54 @@
"@types/he": "^1.1.2",
"@types/jest": "^29.4.0",
"@types/lodash.chunk": "^4.2.7",
"@types/lodash.clonedeep": "^4.5.7",
"@types/lodash.debounce": "^4.0.7",
"@types/lodash.isequal": "^4.5.6",
"@types/lodash.omit": "^4.5.7",
"@types/lodash.once": "^4.1.7",
"@types/lodash.random": "^3.2.7",
"@types/lodash.samplesize": "^4.2.7",
"@types/lodash.set": "^4.3.7",
"@types/lodash.shuffle": "^4.2.7",
"@types/psl": "^1.1.1",
"@types/react-avatar-editor": "^13.0.0",
"@types/react-native": "^0.67.3",
"@types/react-responsive": "^8.0.5",
"@types/react-test-renderer": "^17.0.1",
"@typescript-eslint/eslint-plugin": "^5.48.2",
"@typescript-eslint/parser": "^5.48.2",
"babel-jest": "^29.4.2",
"babel-loader": "^9.1.2",
"babel-plugin-macros": "^3.1.0",
"babel-plugin-module-resolver": "^5.0.0",
"babel-plugin-react-native-web": "^0.18.12",
"detox": "^20.1.2",
"detox": "^20.13.0",
"eslint": "^8.19.0",
"eslint-plugin-detox": "^1.0.0",
"eslint-plugin-ft-flow": "^2.0.3",
"eslint-plugin-react": "^7.33.2",
"eslint-plugin-react-native-a11y": "^3.3.0",
"html-webpack-plugin": "^5.5.0",
"husky": "^8.0.3",
"is-ci": "^3.0.1",
"jest": "^29.4.3",
"jest-expo": "^48.0.2",
"jest-expo": "^49.0.0",
"jest-junit": "^15.0.0",
"lint-staged": "^13.2.3",
"metro-react-native-babel-preset": "^0.73.7",
"prettier": "^2.8.3",
"react-native-dotenv": "^3.3.1",
"react-refresh": "^0.14.0",
"react-scripts": "^5.0.1",
"react-test-renderer": "18.2.0",
"ts-node": "^10.9.1",
"typescript": "^4.4.4",
"typescript": "^5.1.3",
"url-loader": "^4.1.1",
"webpack": "^5.75.0",
"webpack-cli": "^5.0.1",
"webpack-dev-server": "^4.11.1"
},
"resolutions": {
"@types/react": "^17"
"@types/react": "^18",
"**/zeed-dom": "0.10.9"
},
"jest": {
"preset": "jest-expo/ios",
@@ -187,8 +244,11 @@
"json",
"node"
],
"transform": {
"\\.[jt]sx?$": "babel-jest"
},
"transformIgnorePatterns": [
"node_modules/(?!((jest-)?react-native|@react-native(-community)?)|expo(nent)?|@expo(nent)?/.*|@expo-google-fonts/.*|react-navigation|@react-navigation/.*|@unimodules/.*|unimodules|sentry-expo|native-base|normalize-url|react-native-svg)"
"node_modules/(?!((jest-)?react-native|@react-native(-community)?)|expo(nent)?|@expo(nent)?/.*|@expo-google-fonts/.*|react-navigation|nanoid|@react-navigation/.*|@unimodules/.*|unimodules|sentry-expo|native-base|normalize-url|react-native-svg|@sentry/.*|sentry-expo|bcp-47-match)"
],
"modulePathIgnorePatterns": [
"__tests__/.*/__mocks__",
@@ -218,5 +278,8 @@
"last 1 firefox version",
"last 1 safari version"
]
},
"lint-staged": {
"*{.js,.jsx,.ts,.tsx}": "yarn eslint --fix"
}
}
File diff suppressed because it is too large Load Diff
+15
View File
@@ -0,0 +1,15 @@
diff --git a/node_modules/@sentry/react-native/dist/js/utils/ignorerequirecyclelogs.js b/node_modules/@sentry/react-native/dist/js/utils/ignorerequirecyclelogs.js
index 7e0b4cd..177454c 100644
--- a/node_modules/@sentry/react-native/dist/js/utils/ignorerequirecyclelogs.js
+++ b/node_modules/@sentry/react-native/dist/js/utils/ignorerequirecyclelogs.js
@@ -3,6 +3,8 @@ import { LogBox } from 'react-native';
* This is a workaround for using fetch on RN, this is a known issue in react-native and only generates a warning.
*/
export function ignoreRequireCycleLogs() {
- LogBox.ignoreLogs(['Require cycle:']);
+ try {
+ LogBox.ignoreLogs(['Require cycle:']);
+ } catch (e) {}
}
//# sourceMappingURL=ignorerequirecyclelogs.js.map
\ No newline at end of file
+14
View File
@@ -0,0 +1,14 @@
diff --git a/node_modules/babel-preset-expo/index.js b/node_modules/babel-preset-expo/index.js
index 2099ee3..2b9e092 100644
--- a/node_modules/babel-preset-expo/index.js
+++ b/node_modules/babel-preset-expo/index.js
@@ -105,7 +105,8 @@ module.exports = function (api, options = {}) {
],
],
plugins: [
- getObjectRestSpreadPlugin(),
+ // - dan: This will be disabled anyway when we upgrade Expo, but let's do it now.
+ // getObjectRestSpreadPlugin(),
...extraPlugins,
getAliasPlugin(),
[require.resolve('@babel/plugin-proposal-decorators'), { legacy: true }],
+12
View File
@@ -0,0 +1,12 @@
diff --git a/node_modules/babel-preset-fbjs/plugins/inline-requires.js b/node_modules/babel-preset-fbjs/plugins/inline-requires.js
index b11fc83..e18661a 100644
--- a/node_modules/babel-preset-fbjs/plugins/inline-requires.js
+++ b/node_modules/babel-preset-fbjs/plugins/inline-requires.js
@@ -256,6 +256,7 @@ function getInlineableModule(path, state) {
return moduleName == null ||
state.ignoredRequires.has(moduleName) ||
+ moduleName.startsWith('@babel/runtime/') ||
isRequireInScope
? null
: { moduleName, requireFnName: fnName };
+44
View File
@@ -0,0 +1,44 @@
diff --git a/node_modules/metro/src/ModuleGraph/worker/JsFileWrapping.js b/node_modules/metro/src/ModuleGraph/worker/JsFileWrapping.js
index 48a1409..ef185c9 100644
--- a/node_modules/metro/src/ModuleGraph/worker/JsFileWrapping.js
+++ b/node_modules/metro/src/ModuleGraph/worker/JsFileWrapping.js
@@ -70,14 +70,19 @@ function wrapModule(
importDefaultName,
importAllName,
dependencyMapName,
- globalPrefix
+ globalPrefix,
+ moduleFactoryName
) {
const params = buildParameters(
importDefaultName,
importAllName,
dependencyMapName
);
- const factory = functionFromProgram(fileAst.program, params);
+ const factory = functionFromProgram(
+ fileAst.program,
+ params,
+ moduleFactoryName
+ );
const def = t.callExpression(t.identifier(`${globalPrefix}__d`), [factory]);
const ast = t.file(t.program([t.expressionStatement(def)]));
const requireName = renameRequires(ast);
@@ -107,7 +112,16 @@ function wrapJson(source, globalPrefix) {
"});",
].join("\n");
}
-function functionFromProgram(program, parameters) {
+const JS_INVALID_IDENT_RE = /[^a-zA-Z0-9$_]/g;
+function functionFromProgram(program, parameters, moduleFactoryName) {
+ let identifier;
+ if (typeof moduleFactoryName === "string" && moduleFactoryName !== "") {
+ // Keep the name readable so it shows up in profiler traces.
+ // Add an unlikely suffix to avoid collisions with the module code.
+ identifier = t.identifier(
+ `${moduleFactoryName.replace(JS_INVALID_IDENT_RE, "_")}__module_factory__`
+ );
+ }
return t.functionExpression(
undefined,
parameters.map(makeIdentifier),
+50
View File
@@ -0,0 +1,50 @@
diff --git a/node_modules/metro-runtime/src/polyfills/require.js b/node_modules/metro-runtime/src/polyfills/require.js
index ce67cb4..eeeae84 100644
--- a/node_modules/metro-runtime/src/polyfills/require.js
+++ b/node_modules/metro-runtime/src/polyfills/require.js
@@ -22,6 +22,13 @@ global.__c = clear;
global.__registerSegment = registerSegment;
var modules = clear();
+if (__DEV__) {
+ // Added by Dan for module init logging.
+ global.__INIT_LOGS__ = []
+ var initModuleCounter = 0
+ var initModuleStack = []
+}
+
// Don't use a Symbol here, it would pull in an extra polyfill with all sorts of
// additional stuff (e.g. Array.from).
const EMPTY = {};
@@ -303,7 +310,30 @@ function loadModuleImplementation(moduleId, module) {
throw module.error;
}
if (__DEV__) {
- var Systrace = requireSystrace();
+ // Added by Dan for module init logging.
+ var Systrace = {
+ beginEvent(label) {
+ let fullLabel = initModuleCounter++ + ' ' + label
+ global.__INIT_LOGS__.push(
+ ' '.repeat(initModuleStack.length) +
+ ' ENTER ' + fullLabel
+ )
+ initModuleStack.push({
+ fullLabel,
+ startTime: nativePerformanceNow(),
+ })
+ },
+ endEvent() {
+ const res = initModuleStack.pop()
+ const fullLabel = res.fullLabel
+ const startTime = res.startTime
+ const timeElapsed = Math.round(nativePerformanceNow() - startTime)
+ global.__INIT_LOGS__.push(
+ ' '.repeat(initModuleStack.length) +
+ ' LEAVE ' + fullLabel + ' [' + timeElapsed + 'ms]',
+ )
+ }
+ };
var Refresh = requireRefresh();
}
@@ -0,0 +1,41 @@
diff --git a/node_modules/metro-transform-worker/src/index.js b/node_modules/metro-transform-worker/src/index.js
index 27d4cb3..fd71f47 100644
--- a/node_modules/metro-transform-worker/src/index.js
+++ b/node_modules/metro-transform-worker/src/index.js
@@ -190,6 +190,10 @@ async function transformJS(file, { config, options, projectRoot }) {
let dependencyMapName = "";
let dependencies;
let wrappedAst;
+ const minify =
+ options.minify &&
+ options.unstable_transformProfile !== "hermes-canary" &&
+ options.unstable_transformProfile !== "hermes-stable";
// If the module to transform is a script (meaning that is not part of the
// dependency graph and it code will just be prepended to the bundle modules),
@@ -229,19 +233,20 @@ async function transformJS(file, { config, options, projectRoot }) {
if (config.unstable_disableModuleWrapping === true) {
wrappedAst = ast;
} else {
+ let moduleFactoryName;
+ if (options.dev && !minify) {
+ moduleFactoryName = file.filename;
+ }
({ ast: wrappedAst } = JsFileWrapping.wrapModule(
ast,
importDefault,
importAll,
dependencyMapName,
- config.globalPrefix
+ config.globalPrefix,
+ moduleFactoryName
));
}
}
- const minify =
- options.minify &&
- options.unstable_transformProfile !== "hermes-canary" &&
- options.unstable_transformProfile !== "hermes-stable";
const reserved = [];
if (config.unstable_dependencyMapReservedName != null) {
reserved.push(config.unstable_dependencyMapReservedName);
+17
View File
@@ -0,0 +1,17 @@
diff --git a/node_modules/react-native/Libraries/Text/TextInput/RCTBackedTextInputDelegateAdapter.m b/node_modules/react-native/Libraries/Text/TextInput/RCTBackedTextInputDelegateAdapter.m
index 9dca6a5..090bda5 100644
--- a/node_modules/react-native/Libraries/Text/TextInput/RCTBackedTextInputDelegateAdapter.m
+++ b/node_modules/react-native/Libraries/Text/TextInput/RCTBackedTextInputDelegateAdapter.m
@@ -266,11 +266,10 @@ static void *TextFieldSelectionObservingContext = &TextFieldSelectionObservingCo
- (void)textViewDidChangeSelection:(__unused UITextView *)textView
{
- if (_lastStringStateWasUpdatedWith && ![_lastStringStateWasUpdatedWith isEqual:_backedTextInputView.attributedText]) {
+ if (![_lastStringStateWasUpdatedWith isEqual:_backedTextInputView.attributedText]) {
[self textViewDidChange:_backedTextInputView];
_ignoreNextTextInputCall = YES;
}
- _lastStringStateWasUpdatedWith = _backedTextInputView.attributedText;
[self textViewProbablyDidChangeSelection];
}
+4 -4
View File
@@ -1,5 +1,5 @@
diff --git a/node_modules/react-native-pager-view/ios/ReactNativePageView.m b/node_modules/react-native-pager-view/ios/ReactNativePageView.m
index ab0fc7f..fbbf19f 100644
index ab0fc7f..1ace752 100644
--- a/node_modules/react-native-pager-view/ios/ReactNativePageView.m
+++ b/node_modules/react-native-pager-view/ios/ReactNativePageView.m
@@ -1,6 +1,6 @@
@@ -19,7 +19,7 @@ index ab0fc7f..fbbf19f 100644
@property(nonatomic, strong) UIPageViewController *reactPageViewController;
@property(nonatomic, strong) RCTEventDispatcher *eventDispatcher;
@@ -80,6 +80,10 @@
@@ -80,6 +80,10 @@ - (void)didMoveToWindow {
[self setupInitialController];
}
@@ -30,13 +30,13 @@ index ab0fc7f..fbbf19f 100644
if (self.reactViewController.navigationController != nil && self.reactViewController.navigationController.interactivePopGestureRecognizer != nil) {
[self.scrollView.panGestureRecognizer requireGestureRecognizerToFail:self.reactViewController.navigationController.interactivePopGestureRecognizer];
}
@@ -463,4 +467,21 @@
@@ -463,4 +467,21 @@ - (NSString *)determineScrollDirection:(UIScrollView *)scrollView {
- (BOOL)isLtrLayout {
return [_layoutDirection isEqualToString:@"ltr"];
}
+
+- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
+ if (otherGestureRecognizer == self.scrollView.panGestureRecognizer) {
+ if (!_overdrag && otherGestureRecognizer == self.scrollView.panGestureRecognizer) {
+ UIPanGestureRecognizer* p = (UIPanGestureRecognizer*) gestureRecognizer;
+ CGPoint velocity = [p velocityInView:self];
+ if (self.currentIndex == 0 && velocity.x > 0) {
+107 -44
View File
@@ -1,67 +1,130 @@
import 'react-native-url-polyfill/auto'
import 'lib/sentry' // must be near top
import React, {useState, useEffect} from 'react'
import {Linking} from 'react-native'
import {RootSiblingParent} from 'react-native-root-siblings'
import * as SplashScreen from 'expo-splash-screen'
import {SafeAreaProvider} from 'react-native-safe-area-context'
import {GestureHandlerRootView} from 'react-native-gesture-handler'
import {observer} from 'mobx-react-lite'
import {QueryClientProvider} from '@tanstack/react-query'
import {enableFreeze} from 'react-native-screens'
import 'view/icons'
import {init as initPersistedState} from '#/state/persisted'
import {init as initReminders} from '#/state/shell/reminders'
import {listenSessionDropped} from './state/events'
import {useColorMode} from 'state/shell'
import {ThemeProvider} from 'lib/ThemeContext'
import {s} from 'lib/styles'
import * as view from './view/index'
import {RootStoreModel, setupState, RootStoreProvider} from './state'
import {Shell} from './view/shell'
import * as notifee from 'lib/notifee'
import * as analytics from 'lib/analytics'
import * as Toast from './view/com/util/Toast'
import {handleLink} from './Navigation'
import {Shell} from 'view/shell'
import * as notifications from 'lib/notifications/notifications'
import * as analytics from 'lib/analytics/analytics'
import * as Toast from 'view/com/util/Toast'
import {queryClient} from 'lib/react-query'
import {TestCtrls} from 'view/com/testing/TestCtrls'
import {Provider as ShellStateProvider} from 'state/shell'
import {Provider as ModalStateProvider} from 'state/modals'
import {Provider as LightboxStateProvider} from 'state/lightbox'
import {Provider as MutedThreadsProvider} from 'state/muted-threads'
import {Provider as InvitesStateProvider} from 'state/invites'
import {Provider as PrefsStateProvider} from 'state/preferences'
import {
Provider as SessionProvider,
useSession,
useSessionApi,
} from 'state/session'
import {Provider as UnreadNotifsProvider} from 'state/queries/notifications/unread'
import * as persisted from '#/state/persisted'
import {i18n} from '@lingui/core'
import {I18nProvider} from '@lingui/react'
import {messages} from './locale/locales/en/messages'
i18n.load('en', messages)
i18n.activate('en')
const App = observer(() => {
const [rootStore, setRootStore] = useState<RootStoreModel | undefined>(
undefined,
)
enableFreeze(true)
SplashScreen.preventAutoHideAsync()
function InnerApp() {
const colorMode = useColorMode()
const {isInitialLoad} = useSession()
const {resumeSession} = useSessionApi()
// init
useEffect(() => {
view.setup()
setupState().then(store => {
setRootStore(store)
analytics.init(store)
notifee.init(store)
SplashScreen.hideAsync()
Linking.getInitialURL().then((url: string | null) => {
if (url) {
handleLink(url)
}
})
Linking.addEventListener('url', ({url}) => {
handleLink(url)
})
store.onSessionDropped(() => {
Toast.show('Sorry! Your session expired. Please log in again.')
})
initReminders()
analytics.init()
notifications.init(queryClient)
listenSessionDropped(() => {
Toast.show('Sorry! Your session expired. Please log in again.')
})
}, [])
const account = persisted.get('session').currentAccount
resumeSession(account)
}, [resumeSession])
// show nothing prior to init
if (!rootStore) {
if (isInitialLoad) {
// TODO add a loading state
return null
}
/*
* Session and initial state should be loaded prior to rendering below.
*/
return (
<ThemeProvider theme={rootStore.shell.darkMode ? 'dark' : 'light'}>
<RootSiblingParent>
<UnreadNotifsProvider>
<ThemeProvider theme={colorMode}>
<analytics.Provider>
<RootStoreProvider value={rootStore}>
<GestureHandlerRootView style={s.h100pct}>
<SafeAreaProvider>
<I18nProvider i18n={i18n}>
{/* All components should be within this provider */}
<RootSiblingParent>
<GestureHandlerRootView style={s.h100pct}>
<TestCtrls />
<Shell />
</SafeAreaProvider>
</GestureHandlerRootView>
</RootStoreProvider>
</GestureHandlerRootView>
</RootSiblingParent>
</I18nProvider>
</analytics.Provider>
</RootSiblingParent>
</ThemeProvider>
</ThemeProvider>
</UnreadNotifsProvider>
)
})
}
function App() {
const [isReady, setReady] = useState(false)
React.useEffect(() => {
initPersistedState().then(() => setReady(true))
}, [])
if (!isReady) {
return null
}
/*
* NOTE: only nothing here can depend on other data or session state, since
* that is set up in the InnerApp component above.
*/
return (
<QueryClientProvider client={queryClient}>
<SessionProvider>
<ShellStateProvider>
<PrefsStateProvider>
<MutedThreadsProvider>
<InvitesStateProvider>
<ModalStateProvider>
<LightboxStateProvider>
<InnerApp />
</LightboxStateProvider>
</ModalStateProvider>
</InvitesStateProvider>
</MutedThreadsProvider>
</PrefsStateProvider>
</ShellStateProvider>
</SessionProvider>
</QueryClientProvider>
)
}
export default App
+100 -25
View File
@@ -1,42 +1,117 @@
import 'lib/sentry' // must be near top
import React, {useState, useEffect} from 'react'
import {QueryClientProvider} from '@tanstack/react-query'
import {SafeAreaProvider} from 'react-native-safe-area-context'
import {RootSiblingParent} from 'react-native-root-siblings'
import * as view from './view/index'
import * as analytics from 'lib/analytics'
import {RootStoreModel, setupState, RootStoreProvider} from './state'
import {Shell} from './view/shell/index'
import {ToastContainer} from './view/com/util/Toast.web'
import {enableFreeze} from 'react-native-screens'
function App() {
const [rootStore, setRootStore] = useState<RootStoreModel | undefined>(
undefined,
)
import 'view/icons'
import {init as initPersistedState} from '#/state/persisted'
import {init as initReminders} from '#/state/shell/reminders'
import {useColorMode} from 'state/shell'
import * as analytics from 'lib/analytics/analytics'
import {Shell} from 'view/shell/index'
import {ToastContainer} from 'view/com/util/Toast.web'
import {ThemeProvider} from 'lib/ThemeContext'
import {queryClient} from 'lib/react-query'
import {i18n} from '@lingui/core'
import {I18nProvider} from '@lingui/react'
import {defaultLocale, dynamicActivate} from './locale/i18n'
import {Provider as ShellStateProvider} from 'state/shell'
import {Provider as ModalStateProvider} from 'state/modals'
import {Provider as LightboxStateProvider} from 'state/lightbox'
import {Provider as MutedThreadsProvider} from 'state/muted-threads'
import {Provider as InvitesStateProvider} from 'state/invites'
import {Provider as PrefsStateProvider} from 'state/preferences'
import {
Provider as SessionProvider,
useSession,
useSessionApi,
} from 'state/session'
import {Provider as UnreadNotifsProvider} from 'state/queries/notifications/unread'
import * as persisted from '#/state/persisted'
enableFreeze(true)
function InnerApp() {
const {isInitialLoad} = useSession()
const {resumeSession} = useSessionApi()
const colorMode = useColorMode()
// init
useEffect(() => {
view.setup()
setupState().then(store => {
setRootStore(store)
analytics.init(store)
})
}, [])
initReminders()
analytics.init()
dynamicActivate(defaultLocale) // async import of locale data
const account = persisted.get('session').currentAccount
resumeSession(account)
}, [resumeSession])
// show nothing prior to init
if (!rootStore) {
if (isInitialLoad) {
// TODO add a loading state
return null
}
/*
* Session and initial state should be loaded prior to rendering below.
*/
return (
<RootSiblingParent>
<analytics.Provider>
<RootStoreProvider value={rootStore}>
<SafeAreaProvider>
<Shell />
</SafeAreaProvider>
<UnreadNotifsProvider>
<ThemeProvider theme={colorMode}>
<analytics.Provider>
<I18nProvider i18n={i18n}>
{/* All components should be within this provider */}
<RootSiblingParent>
<SafeAreaProvider>
<Shell />
</SafeAreaProvider>
</RootSiblingParent>
</I18nProvider>
<ToastContainer />
</RootStoreProvider>
</analytics.Provider>
</RootSiblingParent>
</analytics.Provider>
</ThemeProvider>
</UnreadNotifsProvider>
)
}
function App() {
const [isReady, setReady] = useState(false)
React.useEffect(() => {
initPersistedState().then(() => setReady(true))
}, [])
if (!isReady) {
return null
}
/*
* NOTE: only nothing here can depend on other data or session state, since
* that is set up in the InnerApp component above.
*/
return (
<QueryClientProvider client={queryClient}>
<SessionProvider>
<ShellStateProvider>
<PrefsStateProvider>
<MutedThreadsProvider>
<InvitesStateProvider>
<ModalStateProvider>
<LightboxStateProvider>
<InnerApp />
</LightboxStateProvider>
</ModalStateProvider>
</InvitesStateProvider>
</MutedThreadsProvider>
</PrefsStateProvider>
</ShellStateProvider>
</SessionProvider>
</QueryClientProvider>
)
}
+345 -39
View File
@@ -1,35 +1,58 @@
import * as React from 'react'
import {StyleSheet} from 'react-native'
import * as SplashScreen from 'expo-splash-screen'
import {
NavigationContainer,
createNavigationContainerRef,
CommonActions,
StackActions,
DefaultTheme,
DarkTheme,
} from '@react-navigation/native'
import {createNativeStackNavigator} from '@react-navigation/native-stack'
import {createBottomTabNavigator} from '@react-navigation/bottom-tabs'
import {
BottomTabBarProps,
createBottomTabNavigator,
} from '@react-navigation/bottom-tabs'
import {
HomeTabNavigatorParams,
SearchTabNavigatorParams,
FeedsTabNavigatorParams,
NotificationsTabNavigatorParams,
FlatNavigatorParams,
AllNavigatorParams,
MyProfileTabNavigatorParams,
BottomTabNavigatorParams,
} from 'lib/routes/types'
import {BottomBar} from './view/shell/BottomBar'
import {BottomBar} from './view/shell/bottom-bar/BottomBar'
import {buildStateObject} from 'lib/routes/helpers'
import {State, RouteParams} from 'lib/routes/types'
import {colors} from 'lib/styles'
import {isNative} from 'platform/detection'
import {useColorSchemeStyle} from 'lib/hooks/useColorSchemeStyle'
import {router} from './routes'
import {usePalette} from 'lib/hooks/usePalette'
import {bskyTitle} from 'lib/strings/headings'
import {JSX} from 'react/jsx-runtime'
import {timeout} from 'lib/async/timeout'
import {useUnreadNotifications} from './state/queries/notifications/unread'
import {HomeScreen} from './view/screens/Home'
import {SearchScreen} from './view/screens/Search'
import {FeedsScreen} from './view/screens/Feeds'
import {NotificationsScreen} from './view/screens/Notifications'
import {ListsScreen} from './view/screens/Lists'
import {ModerationScreen} from './view/screens/Moderation'
import {ModerationModlistsScreen} from './view/screens/ModerationModlists'
import {NotFoundScreen} from './view/screens/NotFound'
import {SettingsScreen} from './view/screens/Settings'
import {LanguageSettingsScreen} from './view/screens/LanguageSettings'
import {ProfileScreen} from './view/screens/Profile'
import {ProfileFollowersScreen} from './view/screens/ProfileFollowers'
import {ProfileFollowsScreen} from './view/screens/ProfileFollows'
import {ProfileFeedScreen} from './view/screens/ProfileFeed'
import {ProfileFeedLikedByScreen} from './view/screens/ProfileFeedLikedBy'
import {ProfileListScreen} from './view/screens/ProfileList'
import {PostThreadScreen} from './view/screens/PostThread'
import {PostLikedByScreen} from './view/screens/PostLikedBy'
import {PostRepostedByScreen} from './view/screens/PostRepostedBy'
@@ -40,43 +63,179 @@ import {PrivacyPolicyScreen} from './view/screens/PrivacyPolicy'
import {TermsOfServiceScreen} from './view/screens/TermsOfService'
import {CommunityGuidelinesScreen} from './view/screens/CommunityGuidelines'
import {CopyrightPolicyScreen} from './view/screens/CopyrightPolicy'
import {AppPasswords} from 'view/screens/AppPasswords'
import {ModerationMutedAccounts} from 'view/screens/ModerationMutedAccounts'
import {ModerationBlockedAccounts} from 'view/screens/ModerationBlockedAccounts'
import {SavedFeeds} from 'view/screens/SavedFeeds'
import {PreferencesHomeFeed} from 'view/screens/PreferencesHomeFeed'
import {PreferencesThreads} from 'view/screens/PreferencesThreads'
const navigationRef = createNavigationContainerRef<AllNavigatorParams>()
const HomeTab = createNativeStackNavigator<HomeTabNavigatorParams>()
const SearchTab = createNativeStackNavigator<SearchTabNavigatorParams>()
const FeedsTab = createNativeStackNavigator<FeedsTabNavigatorParams>()
const NotificationsTab =
createNativeStackNavigator<NotificationsTabNavigatorParams>()
const MyProfileTab = createNativeStackNavigator<MyProfileTabNavigatorParams>()
const Flat = createNativeStackNavigator<FlatNavigatorParams>()
const Tab = createBottomTabNavigator()
const Tab = createBottomTabNavigator<BottomTabNavigatorParams>()
/**
* These "common screens" are reused across stacks.
*/
function commonScreens(Stack: typeof HomeTab) {
function commonScreens(Stack: typeof HomeTab, unreadCountLabel?: string) {
const title = (page: string) => bskyTitle(page, unreadCountLabel)
return (
<>
<Stack.Screen name="NotFound" component={NotFoundScreen} />
<Stack.Screen name="Settings" component={SettingsScreen} />
<Stack.Screen name="Profile" component={ProfileScreen} />
<Stack.Screen
name="NotFound"
getComponent={() => NotFoundScreen}
options={{title: title('Not Found')}}
/>
<Stack.Screen
name="Lists"
component={ListsScreen}
options={{title: title('Lists')}}
/>
<Stack.Screen
name="Moderation"
getComponent={() => ModerationScreen}
options={{title: title('Moderation')}}
/>
<Stack.Screen
name="ModerationModlists"
getComponent={() => ModerationModlistsScreen}
options={{title: title('Moderation Lists')}}
/>
<Stack.Screen
name="ModerationMutedAccounts"
getComponent={() => ModerationMutedAccounts}
options={{title: title('Muted Accounts')}}
/>
<Stack.Screen
name="ModerationBlockedAccounts"
getComponent={() => ModerationBlockedAccounts}
options={{title: title('Blocked Accounts')}}
/>
<Stack.Screen
name="Settings"
getComponent={() => SettingsScreen}
options={{title: title('Settings')}}
/>
<Stack.Screen
name="LanguageSettings"
getComponent={() => LanguageSettingsScreen}
options={{title: title('Language Settings')}}
/>
<Stack.Screen
name="Profile"
getComponent={() => ProfileScreen}
options={({route}) => ({
title: title(`@${route.params.name}`),
animation: 'none',
})}
/>
<Stack.Screen
name="ProfileFollowers"
component={ProfileFollowersScreen}
getComponent={() => ProfileFollowersScreen}
options={({route}) => ({
title: title(`People following @${route.params.name}`),
})}
/>
<Stack.Screen
name="ProfileFollows"
getComponent={() => ProfileFollowsScreen}
options={({route}) => ({
title: title(`People followed by @${route.params.name}`),
})}
/>
<Stack.Screen
name="ProfileList"
getComponent={() => ProfileListScreen}
options={{title: title('List')}}
/>
<Stack.Screen
name="PostThread"
getComponent={() => PostThreadScreen}
options={({route}) => ({title: title(`Post by @${route.params.name}`)})}
/>
<Stack.Screen
name="PostLikedBy"
getComponent={() => PostLikedByScreen}
options={({route}) => ({title: title(`Post by @${route.params.name}`)})}
/>
<Stack.Screen
name="PostRepostedBy"
getComponent={() => PostRepostedByScreen}
options={({route}) => ({title: title(`Post by @${route.params.name}`)})}
/>
<Stack.Screen
name="ProfileFeed"
getComponent={() => ProfileFeedScreen}
options={{title: title('Feed')}}
/>
<Stack.Screen
name="ProfileFeedLikedBy"
getComponent={() => ProfileFeedLikedByScreen}
options={{title: title('Liked by')}}
/>
<Stack.Screen
name="Debug"
getComponent={() => DebugScreen}
options={{title: title('Debug')}}
/>
<Stack.Screen
name="Log"
getComponent={() => LogScreen}
options={{title: title('Log')}}
/>
<Stack.Screen
name="Support"
getComponent={() => SupportScreen}
options={{title: title('Support')}}
/>
<Stack.Screen
name="PrivacyPolicy"
getComponent={() => PrivacyPolicyScreen}
options={{title: title('Privacy Policy')}}
/>
<Stack.Screen
name="TermsOfService"
getComponent={() => TermsOfServiceScreen}
options={{title: title('Terms of Service')}}
/>
<Stack.Screen name="ProfileFollows" component={ProfileFollowsScreen} />
<Stack.Screen name="PostThread" component={PostThreadScreen} />
<Stack.Screen name="PostLikedBy" component={PostLikedByScreen} />
<Stack.Screen name="PostRepostedBy" component={PostRepostedByScreen} />
<Stack.Screen name="Debug" component={DebugScreen} />
<Stack.Screen name="Log" component={LogScreen} />
<Stack.Screen name="Support" component={SupportScreen} />
<Stack.Screen name="PrivacyPolicy" component={PrivacyPolicyScreen} />
<Stack.Screen name="TermsOfService" component={TermsOfServiceScreen} />
<Stack.Screen
name="CommunityGuidelines"
component={CommunityGuidelinesScreen}
getComponent={() => CommunityGuidelinesScreen}
options={{title: title('Community Guidelines')}}
/>
<Stack.Screen
name="CopyrightPolicy"
getComponent={() => CopyrightPolicyScreen}
options={{title: title('Copyright Policy')}}
/>
<Stack.Screen
name="AppPasswords"
getComponent={() => AppPasswords}
options={{title: title('App Passwords')}}
/>
<Stack.Screen
name="SavedFeeds"
getComponent={() => SavedFeeds}
options={{title: title('Edit My Feeds')}}
/>
<Stack.Screen
name="PreferencesHomeFeed"
getComponent={() => PreferencesHomeFeed}
options={{title: title('Home Feed Preferences')}}
/>
<Stack.Screen
name="PreferencesThreads"
getComponent={() => PreferencesThreads}
options={{title: title('Threads Preferences')}}
/>
<Stack.Screen name="CopyrightPolicy" component={CopyrightPolicyScreen} />
</>
)
}
@@ -86,25 +245,37 @@ function commonScreens(Stack: typeof HomeTab) {
* in 3 distinct tab-stacks with a different root screen on each.
*/
function TabsNavigator() {
const tabBar = React.useCallback(props => <BottomBar {...props} />, [])
const tabBar = React.useCallback(
(props: JSX.IntrinsicAttributes & BottomTabBarProps) => (
<BottomBar {...props} />
),
[],
)
return (
<Tab.Navigator
initialRouteName="HomeTab"
backBehavior="initialRoute"
screenOptions={{headerShown: false}}
screenOptions={{headerShown: false, lazy: true}}
tabBar={tabBar}>
<Tab.Screen name="HomeTab" component={HomeTabNavigator} />
<Tab.Screen name="HomeTab" getComponent={() => HomeTabNavigator} />
<Tab.Screen name="SearchTab" getComponent={() => SearchTabNavigator} />
<Tab.Screen name="FeedsTab" getComponent={() => FeedsTabNavigator} />
<Tab.Screen
name="NotificationsTab"
component={NotificationsTabNavigator}
getComponent={() => NotificationsTabNavigator}
/>
<Tab.Screen
name="MyProfileTab"
getComponent={() => MyProfileTabNavigator}
/>
<Tab.Screen name="SearchTab" component={SearchTabNavigator} />
</Tab.Navigator>
)
}
function HomeTabNavigator() {
const contentStyle = useColorSchemeStyle(styles.bgLight, styles.bgDark)
return (
<HomeTab.Navigator
screenOptions={{
@@ -114,7 +285,7 @@ function HomeTabNavigator() {
animationDuration: 250,
contentStyle,
}}>
<HomeTab.Screen name="Home" component={HomeScreen} />
<HomeTab.Screen name="Home" getComponent={() => HomeScreen} />
{commonScreens(HomeTab)}
</HomeTab.Navigator>
)
@@ -131,12 +302,29 @@ function SearchTabNavigator() {
animationDuration: 250,
contentStyle,
}}>
<SearchTab.Screen name="Search" component={SearchScreen} />
<SearchTab.Screen name="Search" getComponent={() => SearchScreen} />
{commonScreens(SearchTab as typeof HomeTab)}
</SearchTab.Navigator>
)
}
function FeedsTabNavigator() {
const contentStyle = useColorSchemeStyle(styles.bgLight, styles.bgDark)
return (
<FeedsTab.Navigator
screenOptions={{
gestureEnabled: true,
fullScreenGestureEnabled: true,
headerShown: false,
animationDuration: 250,
contentStyle,
}}>
<FeedsTab.Screen name="Feeds" getComponent={() => FeedsScreen} />
{commonScreens(FeedsTab as typeof HomeTab)}
</FeedsTab.Navigator>
)
}
function NotificationsTabNavigator() {
const contentStyle = useColorSchemeStyle(styles.bgLight, styles.bgDark)
return (
@@ -150,18 +338,46 @@ function NotificationsTabNavigator() {
}}>
<NotificationsTab.Screen
name="Notifications"
component={NotificationsScreen}
getComponent={() => NotificationsScreen}
/>
{commonScreens(NotificationsTab as typeof HomeTab)}
</NotificationsTab.Navigator>
)
}
function MyProfileTabNavigator() {
const contentStyle = useColorSchemeStyle(styles.bgLight, styles.bgDark)
return (
<MyProfileTab.Navigator
screenOptions={{
gestureEnabled: true,
fullScreenGestureEnabled: true,
headerShown: false,
animationDuration: 250,
contentStyle,
}}>
<MyProfileTab.Screen
name="MyProfile"
// @ts-ignore // TODO: fix this broken type in ProfileScreen
getComponent={() => ProfileScreen}
initialParams={{
name: 'me',
}}
/>
{commonScreens(MyProfileTab as typeof HomeTab)}
</MyProfileTab.Navigator>
)
}
/**
* The FlatNavigator is used by Web to represent the routes
* in a single ("flat") stack.
*/
function FlatNavigator() {
const FlatNavigator = () => {
const pal = usePalette('default')
const numUnread = useUnreadNotifications()
const title = (page: string) => bskyTitle(page, numUnread)
return (
<Flat.Navigator
screenOptions={{
@@ -169,12 +385,29 @@ function FlatNavigator() {
fullScreenGestureEnabled: true,
headerShown: false,
animationDuration: 250,
contentStyle: {backgroundColor: 'white'},
contentStyle: [pal.view],
}}>
<Flat.Screen name="Home" component={HomeScreen} />
<Flat.Screen name="Search" component={SearchScreen} />
<Flat.Screen name="Notifications" component={NotificationsScreen} />
{commonScreens(Flat as typeof HomeTab)}
<Flat.Screen
name="Home"
getComponent={() => HomeScreen}
options={{title: title('Home')}}
/>
<Flat.Screen
name="Search"
getComponent={() => SearchScreen}
options={{title: title('Search')}}
/>
<Flat.Screen
name="Feeds"
getComponent={() => FeedsScreen}
options={{title: title('Feeds')}}
/>
<Flat.Screen
name="Notifications"
getComponent={() => NotificationsScreen}
options={{title: title('Notifications')}}
/>
{commonScreens(Flat as typeof HomeTab, numUnread)}
</Flat.Navigator>
)
}
@@ -211,7 +444,16 @@ const LINKING = {
if (name === 'Notifications') {
return buildStateObject('NotificationsTab', 'Notifications', params)
}
return buildStateObject('HomeTab', name, params)
if (name === 'Home') {
return buildStateObject('HomeTab', 'Home', params)
}
// if the path is something else, like a post, profile, or even settings, we need to initialize the home tab as pre-existing state otherwise the back button will not work
return buildStateObject('HomeTab', name, params, [
{
name: 'Home',
params: {},
},
])
} else {
return buildStateObject('Flat', name, params)
}
@@ -219,8 +461,21 @@ const LINKING = {
}
function RoutesContainer({children}: React.PropsWithChildren<{}>) {
const theme = useColorSchemeStyle(DefaultTheme, DarkTheme)
return (
<NavigationContainer ref={navigationRef} linking={LINKING}>
<NavigationContainer
ref={navigationRef}
linking={LINKING}
theme={theme}
onReady={() => {
SplashScreen.hideAsync()
const initMs = Math.round(
// @ts-ignore Emitted by Metro in the bundle prelude
performance.now() - global.__BUNDLE_START_TIME__,
)
console.log(`Time to first paint: ${initMs} ms`)
logModuleInitTrace()
}}>
{children}
</NavigationContainer>
)
@@ -236,15 +491,53 @@ function navigate<K extends keyof AllNavigatorParams>(
params?: AllNavigatorParams[K],
) {
if (navigationRef.isReady()) {
// @ts-ignore I dont know what would make typescript happy but I have a life -prf
navigationRef.navigate(name, params)
return Promise.race([
new Promise<void>(resolve => {
const handler = () => {
resolve()
navigationRef.removeListener('state', handler)
}
navigationRef.addListener('state', handler)
// @ts-ignore I dont know what would make typescript happy but I have a life -prf
navigationRef.navigate(name, params)
}),
timeout(1e3),
])
}
return Promise.resolve()
}
function resetToTab(tabName: 'HomeTab' | 'SearchTab' | 'NotificationsTab') {
if (navigationRef.isReady()) {
navigate(tabName)
navigationRef.dispatch(StackActions.popToTop())
if (navigationRef.canGoBack()) {
navigationRef.dispatch(StackActions.popToTop()) //we need to check .canGoBack() before calling it
}
}
}
// returns a promise that resolves after the state reset is complete
function reset(): Promise<void> {
if (navigationRef.isReady()) {
navigationRef.dispatch(
CommonActions.reset({
index: 0,
routes: [{name: isNative ? 'HomeTab' : 'Home'}],
}),
)
return Promise.race([
timeout(1e3),
new Promise<void>(resolve => {
const handler = () => {
resolve()
navigationRef.removeListener('state', handler)
}
navigationRef.addListener('state', handler)
}),
])
} else {
return Promise.resolve()
}
}
@@ -286,13 +579,26 @@ const styles = StyleSheet.create({
backgroundColor: colors.black,
},
bgLight: {
backgroundColor: colors.gray1,
backgroundColor: colors.white,
},
})
function logModuleInitTrace() {
if (__DEV__) {
// This log is noisy, so keep false committed
const shouldLog = false
// Relies on our patch to polyfill.js in metro-runtime
const initLogs = (global as any).__INIT_LOGS__
if (shouldLog && Array.isArray(initLogs)) {
console.log(initLogs.join('\n'))
}
}
}
export {
navigate,
resetToTab,
reset,
handleLink,
TabsNavigator,
FlatNavigator,
+9
View File
@@ -0,0 +1,9 @@
export const IS_TEST = process.env.NODE_ENV === 'test'
export const IS_DEV = __DEV__
export const IS_PROD = !IS_DEV
export const LOG_DEBUG = process.env.EXPO_PUBLIC_LOG_DEBUG || ''
export const LOG_LEVEL = (process.env.EXPO_PUBLIC_LOG_LEVEL || 'info') as
| 'debug'
| 'info'
| 'warn'
| 'error'
+45 -9
View File
@@ -1,5 +1,12 @@
import React, {createContext, useContext, useMemo} from 'react'
import {TextStyle, useColorScheme, ViewStyle} from 'react-native'
import {isWeb} from 'platform/detection'
import React, {ReactNode, createContext, useContext} from 'react'
import {
AppState,
TextStyle,
useColorScheme as useColorScheme_BUGGY,
ViewStyle,
ColorSchemeName,
} from 'react-native'
import {darkTheme, defaultTheme} from './themes'
export type ColorScheme = 'light' | 'dark'
@@ -78,23 +85,52 @@ export interface Theme {
}
export interface ThemeProviderProps {
theme?: ColorScheme
children?: ReactNode
theme?: 'light' | 'dark' | 'system'
}
export const ThemeContext = createContext<Theme>(defaultTheme)
export const useTheme = () => useContext(ThemeContext)
function getTheme(theme: ColorSchemeName) {
return theme === 'dark' ? darkTheme : defaultTheme
}
/**
* With RN iOS, we can only "trust" the color scheme reported while the app is
* active. This is a workaround until the bug is fixed upstream.
*
* @see https://github.com/bluesky-social/social-app/pull/1417#issuecomment-1719868504
* @see https://github.com/facebook/react-native/pull/39439
*/
function useColorScheme_FIXED() {
const colorScheme = useColorScheme_BUGGY()
const [currentColorScheme, setCurrentColorScheme] =
React.useState<ColorSchemeName>(colorScheme)
React.useEffect(() => {
// we don't need to be updating state on web
if (isWeb) return
const subscription = AppState.addEventListener('change', state => {
const isActive = state === 'active'
if (!isActive) return
setCurrentColorScheme(colorScheme)
})
return () => subscription.remove()
}, [colorScheme])
return isWeb ? colorScheme : currentColorScheme
}
export const ThemeProvider: React.FC<ThemeProviderProps> = ({
theme,
children,
}) => {
const colorScheme = useColorScheme()
const colorScheme = useColorScheme_FIXED()
const themeValue = getTheme(theme === 'system' ? colorScheme : theme)
const value = useMemo(
() => ((theme || colorScheme) === 'dark' ? darkTheme : defaultTheme),
[colorScheme, theme],
return (
<ThemeContext.Provider value={themeValue}>{children}</ThemeContext.Provider>
)
return <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>
}
-118
View File
@@ -1,118 +0,0 @@
import React from 'react'
import {AppState, AppStateStatus} from 'react-native'
import {
createClient,
AnalyticsProvider,
useAnalytics as useAnalyticsOrig,
} from '@segment/analytics-react-native'
import {RootStoreModel, AppInfo} from 'state/models/root-store'
import {useStores} from 'state/models/root-store'
import {sha256} from 'js-sha256'
const segmentClient = createClient({
writeKey: '8I6DsgfiSLuoONyaunGoiQM7A6y2ybdI',
trackAppLifecycleEvents: false,
})
export function useAnalytics() {
const store = useStores()
const methods = useAnalyticsOrig()
return React.useMemo(() => {
if (store.session.hasSession) {
return methods
}
// dont send analytics pings for anonymous users
return {
screen: () => {},
track: () => {},
identify: () => {},
flush: () => {},
group: () => {},
alias: () => {},
reset: () => {},
}
}, [store, methods])
}
export function init(store: RootStoreModel) {
store.onSessionLoaded(() => {
const sess = store.session.currentSession
if (sess) {
if (sess.email) {
store.log.debug('Ping w/hash')
const email_hashed = sha256(sess.email)
segmentClient.identify(email_hashed, {email_hashed})
} else {
store.log.debug('Ping w/o hash')
segmentClient.identify()
}
}
})
// NOTE
// this is a copy of segment's own lifecycle event tracking
// we handle it manually to ensure that it never fires while the app is backgrounded
// -prf
segmentClient.isReady.onChange(() => {
if (AppState.currentState !== 'active') {
store.log.debug('Prevented a metrics ping while the app was backgrounded')
return
}
const context = segmentClient.context.get()
if (typeof context?.app === 'undefined') {
store.log.debug('Aborted metrics ping due to unavailable context')
return
}
const oldAppInfo = store.appInfo
const newAppInfo = context.app as AppInfo
store.setAppInfo(newAppInfo)
store.log.debug('Recording app info', {new: newAppInfo, old: oldAppInfo})
if (typeof oldAppInfo === 'undefined') {
if (store.session.hasSession) {
segmentClient.track('Application Installed', {
version: newAppInfo.version,
build: newAppInfo.build,
})
}
} else if (newAppInfo.version !== oldAppInfo.version) {
if (store.session.hasSession) {
segmentClient.track('Application Updated', {
version: newAppInfo.version,
build: newAppInfo.build,
previous_version: oldAppInfo.version,
previous_build: oldAppInfo.build,
})
}
}
if (store.session.hasSession) {
segmentClient.track('Application Opened', {
from_background: false,
version: newAppInfo.version,
build: newAppInfo.build,
})
}
})
let lastState: AppStateStatus = AppState.currentState
AppState.addEventListener('change', (state: AppStateStatus) => {
if (state === 'active' && lastState !== 'active') {
const context = segmentClient.context.get()
segmentClient.track('Application Opened', {
from_background: true,
version: context?.app?.version,
build: context?.app?.build,
})
} else if (state !== 'active' && lastState === 'active') {
segmentClient.track('Application Backgrounded')
}
lastState = state
})
}
export function Provider({children}: React.PropsWithChildren<{}>) {
return (
<AnalyticsProvider client={segmentClient}>{children}</AnalyticsProvider>
)
}
+146
View File
@@ -0,0 +1,146 @@
import React from 'react'
import {AppState, AppStateStatus} from 'react-native'
import AsyncStorage from '@react-native-async-storage/async-storage'
import {
createClient,
AnalyticsProvider,
useAnalytics as useAnalyticsOrig,
ClientMethods,
} from '@segment/analytics-react-native'
import {z} from 'zod'
import {useSession} from '#/state/session'
import {sha256} from 'js-sha256'
import {ScreenEvent, TrackEvent} from './types'
import {logger} from '#/logger'
import {listenSessionLoaded} from '#/state/events'
export const appInfo = z.object({
build: z.string().optional(),
name: z.string().optional(),
namespace: z.string().optional(),
version: z.string().optional(),
})
export type AppInfo = z.infer<typeof appInfo>
const segmentClient = createClient({
writeKey: '8I6DsgfiSLuoONyaunGoiQM7A6y2ybdI',
trackAppLifecycleEvents: false,
proxy: 'https://api.events.bsky.app/v1',
})
export const track = segmentClient?.track?.bind?.(segmentClient) as TrackEvent
export function useAnalytics() {
const {hasSession} = useSession()
const methods: ClientMethods = useAnalyticsOrig()
return React.useMemo(() => {
if (hasSession) {
return {
screen: methods.screen as ScreenEvent, // ScreenEvents defines all the possible screen names
track: methods.track as TrackEvent, // TrackEvents defines all the possible track events and their properties
identify: methods.identify,
flush: methods.flush,
group: methods.group,
alias: methods.alias,
reset: methods.reset,
}
}
// dont send analytics pings for anonymous users
return {
screen: () => Promise<void>,
track: () => Promise<void>,
identify: () => Promise<void>,
flush: () => Promise<void>,
group: () => Promise<void>,
alias: () => Promise<void>,
reset: () => Promise<void>,
}
}, [hasSession, methods])
}
export function init() {
listenSessionLoaded(account => {
if (account.did) {
const did_hashed = sha256(account.did)
segmentClient.identify(did_hashed, {did_hashed})
logger.debug('Ping w/hash')
} else {
logger.debug('Ping w/o hash')
segmentClient.identify()
}
})
// NOTE
// this is a copy of segment's own lifecycle event tracking
// we handle it manually to ensure that it never fires while the app is backgrounded
// -prf
segmentClient.isReady.onChange(async () => {
if (AppState.currentState !== 'active') {
logger.debug('Prevented a metrics ping while the app was backgrounded')
return
}
const context = segmentClient.context.get()
if (typeof context?.app === 'undefined') {
logger.debug('Aborted metrics ping due to unavailable context')
return
}
const oldAppInfo = await readAppInfo()
const newAppInfo = context.app as AppInfo
writeAppInfo(newAppInfo)
logger.debug('Recording app info', {new: newAppInfo, old: oldAppInfo})
if (typeof oldAppInfo === 'undefined') {
segmentClient.track('Application Installed', {
version: newAppInfo.version,
build: newAppInfo.build,
})
} else if (newAppInfo.version !== oldAppInfo.version) {
segmentClient.track('Application Updated', {
version: newAppInfo.version,
build: newAppInfo.build,
previous_version: oldAppInfo.version,
previous_build: oldAppInfo.build,
})
}
segmentClient.track('Application Opened', {
from_background: false,
version: newAppInfo.version,
build: newAppInfo.build,
})
})
let lastState: AppStateStatus = AppState.currentState
AppState.addEventListener('change', (state: AppStateStatus) => {
if (state === 'active' && lastState !== 'active') {
const context = segmentClient.context.get()
segmentClient.track('Application Opened', {
from_background: true,
version: context?.app?.version,
build: context?.app?.build,
})
} else if (state !== 'active' && lastState === 'active') {
segmentClient.track('Application Backgrounded')
}
lastState = state
})
}
export function Provider({children}: React.PropsWithChildren<{}>) {
return (
<AnalyticsProvider client={segmentClient}>{children}</AnalyticsProvider>
)
}
async function writeAppInfo(value: AppInfo) {
await AsyncStorage.setItem('BSKY_APP_INFO', JSON.stringify(value))
}
async function readAppInfo(): Promise<AppInfo | undefined> {
const rawData = await AsyncStorage.getItem('BSKY_APP_INFO')
const obj = rawData ? JSON.parse(rawData) : undefined
if (!obj || typeof obj !== 'object') {
return undefined
}
return obj
}
@@ -4,10 +4,12 @@ import {
AnalyticsProvider,
useAnalytics as useAnalyticsOrig,
} from '@segment/analytics-react'
import {RootStoreModel} from 'state/models/root-store'
import {useStores} from 'state/models/root-store'
import {sha256} from 'js-sha256'
import {useSession} from '#/state/session'
import {logger} from '#/logger'
import {listenSessionLoaded} from '#/state/events'
const segmentClient = createClient(
{
writeKey: '8I6DsgfiSLuoONyaunGoiQM7A6y2ybdI',
@@ -15,7 +17,7 @@ const segmentClient = createClient(
{
integrations: {
'Segment.io': {
apiHost: 'api.evt.bsky.app/v1',
apiHost: 'api.events.bsky.app/v1',
},
},
},
@@ -23,10 +25,10 @@ const segmentClient = createClient(
export const track = segmentClient?.track?.bind?.(segmentClient)
export function useAnalytics() {
const store = useStores()
const {hasSession} = useSession()
const methods = useAnalyticsOrig()
return React.useMemo(() => {
if (store.session.hasSession) {
if (hasSession) {
return methods
}
// dont send analytics pings for anonymous users
@@ -39,19 +41,18 @@ export function useAnalytics() {
alias: () => {},
reset: () => {},
}
}, [store, methods])
}, [hasSession, methods])
}
export function init(store: RootStoreModel) {
store.onSessionLoaded(() => {
const sess = store.session.currentSession
if (sess) {
if (sess.email) {
store.log.debug('Ping w/hash')
const email_hashed = sha256(sess.email)
segmentClient.identify(email_hashed, {email_hashed})
export function init() {
listenSessionLoaded(account => {
if (account.did) {
if (account.did) {
const did_hashed = sha256(account.did)
segmentClient.identify(did_hashed, {did_hashed})
logger.debug('Ping w/hash')
} else {
store.log.debug('Ping w/o hash')
logger.debug('Ping w/o hash')
segmentClient.identify()
}
}
+156
View File
@@ -0,0 +1,156 @@
export type TrackEvent = (
event: keyof TrackPropertiesMap,
properties?: TrackPropertiesMap[keyof TrackPropertiesMap],
) => Promise<void>
export type ScreenEvent = (
name: keyof ScreenPropertiesMap,
properties?: ScreenPropertiesMap[keyof ScreenPropertiesMap],
) => Promise<void>
interface TrackPropertiesMap {
// LOGIN / SIGN UP events
'Sign In': {resumedSession: boolean} // CAN BE SERVER
'Create Account': {} // CAN BE SERVER
'Try Create Account': {}
'Create Account Successfully': {}
'Signin:PressedForgotPassword': {}
'Signin:PressedSelectService': {}
// COMPOSER / CREATE POST events
'Create Post': {imageCount: string} // CAN BE SERVER
'Composer:PastedPhotos': {}
'Composer:CameraOpened': {}
'Composer:GalleryOpened': {}
'HomeScreen:PressCompose': {}
'ProfileScreen:PressCompose': {}
// EDIT PROFILE events
'EditHandle:ViewCustomForm': {}
'EditHandle:ViewProvidedForm': {}
'EditHandle:SetNewHandle': {}
'EditProfile:AvatarSelected': {}
'EditProfile:BannerSelected': {}
'EditProfile:Save': {} // CAN BE SERVER
// FEED events
'Feed:onRefresh': {}
'Feed:onEndReached': {}
// POST events
'Post:Like': {} // CAN BE SERVER
'Post:Unlike': {} // CAN BE SERVER
'Post:Repost': {} // CAN BE SERVER
'Post:Unrepost': {} // CAN BE SERVER
'Post:Delete': {} // CAN BE SERVER
'Post:ThreadMute': {} // CAN BE SERVER
'Post:ThreadUnmute': {} // CAN BE SERVER
'Post:Reply': {} // CAN BE SERVER
// FEED ITEM events
'FeedItem:PostReply': {} // CAN BE SERVER
'FeedItem:PostRepost': {} // CAN BE SERVER
'FeedItem:PostLike': {} // CAN BE SERVER
'FeedItem:PostDelete': {} // CAN BE SERVER
'FeedItem:ThreadMute': {} // CAN BE SERVER
// PROFILE events
'Profile:Follow': {
username: string
}
'Profile:Unfollow': {
username: string
}
// PROFILE HEADER events
'ProfileHeader:EditProfileButtonClicked': {}
'ProfileHeader:FollowersButtonClicked': {
handle: string
}
'ProfileHeader:FollowsButtonClicked': {
handle: string
}
'ProfileHeader:ShareButtonClicked': {}
'ProfileHeader:MuteAccountButtonClicked': {}
'ProfileHeader:UnmuteAccountButtonClicked': {}
'ProfileHeader:ReportAccountButtonClicked': {}
'ProfileHeader:AddToListsButtonClicked': {}
'ProfileHeader:BlockAccountButtonClicked': {}
'ProfileHeader:UnblockAccountButtonClicked': {}
'ProfileHeader:FollowButtonClicked': {}
'ProfileHeader:UnfollowButtonClicked': {}
'ProfileHeader:SuggestedFollowsOpened': {}
'ProfileHeader:SuggestedFollowFollowed': {}
'ViewHeader:MenuButtonClicked': {}
// SETTINGS events
'Settings:SwitchAccountButtonClicked': {}
'Settings:AddAccountButtonClicked': {}
'Settings:ChangeHandleButtonClicked': {}
'Settings:InvitecodesButtonClicked': {}
'Settings:ContentfilteringButtonClicked': {}
'Settings:SignOutButtonClicked': {}
'Settings:ContentlanguagesButtonClicked': {}
// MENU events
'Menu:ItemClicked': {url: string}
'Menu:FeedbackClicked': {}
'Menu:HelpClicked': {}
// MOBILE SHELL events
'MobileShell:MyProfileButtonPressed': {}
'MobileShell:HomeButtonPressed': {}
'MobileShell:SearchButtonPressed': {}
'MobileShell:NotificationsButtonPressed': {}
'MobileShell:FeedsButtonPressed': {}
// NOTIFICATIONS events
'Notificatons:OpenApp': {}
// LISTS events
'Lists:onRefresh': {}
'Lists:onEndReached': {}
'CreateList:AvatarSelected': {}
'CreateList:SaveCurateList': {} // CAN BE SERVER
'CreateList:SaveModList': {} // CAN BE SERVER
'Lists:Mute': {} // CAN BE SERVER
'Lists:Unmute': {} // CAN BE SERVER
'Lists:Block': {} // CAN BE SERVER
'Lists:Unblock': {} // CAN BE SERVER
// CUSTOM FEED events
'CustomFeed:Save': {}
'CustomFeed:Unsave': {}
'CustomFeed:Like': {}
'CustomFeed:Unlike': {}
'CustomFeed:Share': {}
'CustomFeed:Pin': {
uri: string
name: string
}
'CustomFeed:Unpin': {
uri: string
name: string
}
'CustomFeed:Reorder': {
uri: string
name: string
index: number
}
'CustomFeed:LoadMore': {}
'MultiFeed:onEndReached': {}
'MultiFeed:onRefresh': {}
// MODERATION events
'Moderation:ContentfilteringButtonClicked': {}
// ONBOARDING events
'Onboarding:Begin': {}
'Onboarding:Complete': {}
'Onboarding:Skipped': {}
'Onboarding:Reset': {}
'Onboarding:SuggestedFollowFollowed': {}
}
interface ScreenPropertiesMap {
Login: {}
CreateAccount: {}
'Choose Account': {}
'Signin:ForgotPassword': {}
'Signin:SetNewPasswordForm': {}
'Signin:PasswordUpdatedForm': {}
Feed: {}
Notifications: {}
Profile: {}
'Profile:Preview': {}
Settings: {}
AppPasswords: {}
Moderation: {}
BlockedAccounts: {}
MutedAccounts: {}
SavedFeeds: {}
}
+1 -1
View File
@@ -11,7 +11,7 @@ export function doPolyfill() {
interface FetchHandlerResponse {
status: number
headers: Record<string, string>
body: ArrayBuffer | undefined
body: any
}
async function fetchHandler(
-137
View File
@@ -1,137 +0,0 @@
import {RootStoreModel} from 'state/index'
import {
AppBskyFeedDefs,
AppBskyFeedGetAuthorFeed as GetAuthorFeed,
} from '@atproto/api'
type ReasonRepost = AppBskyFeedDefs.ReasonRepost
async function getMultipleAuthorsPosts(
rootStore: RootStoreModel,
authors: string[],
cursor: string | undefined = undefined,
limit: number = 10,
) {
const responses = await Promise.all(
authors.map((actor, index) =>
rootStore.agent
.getAuthorFeed({
actor,
limit,
cursor: cursor ? cursor.split(',')[index] : undefined,
})
.catch(_err => ({success: false, headers: {}, data: {feed: []}})),
),
)
return responses
}
function mergePosts(
responses: GetAuthorFeed.Response[],
{repostsOnly, bestOfOnly}: {repostsOnly?: boolean; bestOfOnly?: boolean},
) {
let posts: AppBskyFeedDefs.FeedViewPost[] = []
if (bestOfOnly) {
for (const res of responses) {
if (res.success) {
// filter the feed down to the post with the most likes
res.data.feed = res.data.feed.reduce(
(acc: AppBskyFeedDefs.FeedViewPost[], v) => {
if (
!acc?.[0] &&
!v.reason &&
!v.reply &&
isRecentEnough(v.post.indexedAt)
) {
return [v]
}
if (
acc &&
!v.reason &&
!v.reply &&
(v.post.likeCount || 0) > (acc[0]?.post.likeCount || 0) &&
isRecentEnough(v.post.indexedAt)
) {
return [v]
}
return acc
},
[],
)
}
}
}
// merge into one array
for (const res of responses) {
if (res.success) {
posts = posts.concat(res.data.feed)
}
}
// filter down to reposts of other users
const uris = new Set()
posts = posts.filter(p => {
if (repostsOnly && !isARepostOfSomeoneElse(p)) {
return false
}
if (uris.has(p.post.uri)) {
return false
}
uris.add(p.post.uri)
return true
})
// sort by index time
posts.sort((a, b) => {
return (
Number(new Date(b.post.indexedAt)) - Number(new Date(a.post.indexedAt))
)
})
return posts
}
function isARepostOfSomeoneElse(post: AppBskyFeedDefs.FeedViewPost): boolean {
return (
post.reason?.$type === 'app.bsky.feed.defs#reasonRepost' &&
post.post.author.did !== (post.reason as ReasonRepost).by.did
)
}
function getCombinedCursors(responses: GetAuthorFeed.Response[]) {
let hasCursor = false
const cursors = responses.map(r => {
if (r.data.cursor) {
hasCursor = true
return r.data.cursor
}
return ''
})
if (!hasCursor) {
return undefined
}
const combinedCursors = cursors.join(',')
return combinedCursors
}
function isCombinedCursor(cursor: string) {
return cursor.includes(',')
}
const TWO_DAYS_AGO = Date.now() - 1e3 * 60 * 60 * 48
function isRecentEnough(date: string) {
try {
const d = Number(new Date(date))
return d > TWO_DAYS_AGO
} catch {
return false
}
}
export {
getMultipleAuthorsPosts,
mergePosts,
getCombinedCursors,
isCombinedCursor,
}
+60
View File
@@ -0,0 +1,60 @@
/**
* APP-700
*
* This is a temporary debug setting we're running on the Web build to
* help the protocol team test some changes.
*
* It should be removed in ~2 weeks. It should only be used on the Web
* version of the app.
*/
import {useState, useCallback, useEffect} from 'react'
import {BskyAgent} from '@atproto/api'
import * as Storage from 'lib/storage'
export function useDebugHeaderSetting(agent: BskyAgent): [boolean, () => void] {
const [enabled, setEnabled] = useState<boolean>(false)
useEffect(() => {
async function check() {
if (await isEnabled()) {
setEnabled(true)
}
}
check()
}, [])
const toggle = useCallback(() => {
if (!enabled) {
Storage.saveString('set-header-x-appview-proxy', 'yes')
agent.api.xrpc.setHeader('x-appview-proxy', 'true')
setEnabled(true)
} else {
Storage.remove('set-header-x-appview-proxy')
agent.api.xrpc.unsetHeader('x-appview-proxy')
setEnabled(false)
}
}, [setEnabled, enabled, agent])
return [enabled, toggle]
}
export function setDebugHeader(agent: BskyAgent, enabled: boolean) {
if (enabled) {
Storage.saveString('set-header-x-appview-proxy', 'yes')
agent.api.xrpc.setHeader('x-appview-proxy', 'true')
} else {
Storage.remove('set-header-x-appview-proxy')
agent.api.xrpc.unsetHeader('x-appview-proxy')
}
}
export async function applyDebugHeader(agent: BskyAgent) {
if (await isEnabled()) {
agent.api.xrpc.setHeader('x-appview-proxy', 'true')
}
}
async function isEnabled() {
return (await Storage.loadString('set-header-x-appview-proxy')) === 'yes'
}
+195 -74
View File
@@ -1,7 +1,11 @@
import {AppBskyFeedDefs} from '@atproto/api'
import lande from 'lande'
import {hasProp} from 'lib/type-guards'
import {LANGUAGES_MAP_CODE2} from '../../locale/languages'
import {
AppBskyFeedDefs,
AppBskyFeedPost,
AppBskyEmbedRecordWithMedia,
AppBskyEmbedRecord,
} from '@atproto/api'
import {ReasonFeedSource} from './feed/types'
import {isPostInLanguage} from '../../locale/helpers'
type FeedViewPost = AppBskyFeedDefs.FeedViewPost
export type FeedTunerFn = (
@@ -14,6 +18,12 @@ export class FeedViewPostsSlice {
constructor(public items: FeedViewPost[] = []) {}
get _reactKey() {
return `slice-${this.items[0].post.uri}-${
this.items[0].reason?.indexedAt || this.items[0].post.indexedAt
}`
}
get uri() {
if (this.isFlattenedReply) {
return this.items[1].post.uri
@@ -48,6 +58,18 @@ export class FeedViewPostsSlice {
return this.items[0]
}
get isReply() {
return (
AppBskyFeedPost.isRecord(this.rootItem.post.record) &&
!!this.rootItem.post.record.reply
)
}
get source(): ReasonFeedSource | undefined {
return this.items.find(item => '__source' in item && !!item.__source)
?.__source as ReasonFeedSource
}
containsUri(uri: string) {
return !!this.items.find(item => item.post.uri === uri)
}
@@ -67,11 +89,42 @@ export class FeedViewPostsSlice {
}
flattenReplyParent() {
if (this.items[0].reply?.parent) {
this.isFlattenedReply = true
this.items.splice(0, 0, {post: this.items[0].reply?.parent})
if (this.items[0].reply) {
const reply = this.items[0].reply
if (AppBskyFeedDefs.isPostView(reply.parent)) {
this.isFlattenedReply = true
this.items.splice(0, 0, {post: reply.parent})
}
}
}
isFollowingAllAuthors(userDid: string) {
const item = this.rootItem
if (item.post.author.did === userDid) {
return true
}
if (AppBskyFeedDefs.isPostView(item.reply?.parent)) {
const parent = item.reply?.parent
if (parent?.author.did === userDid) {
return true
}
return (
parent?.author.viewer?.following && item.post.author.viewer?.following
)
}
return false
}
}
export class NoopFeedTuner {
reset() {}
tune(
feed: FeedViewPost[],
_tunerFns: FeedTunerFn[] = [],
_opts?: {dryRun: boolean; maintainOrder: boolean},
): FeedViewPostsSlice[] {
return feed.map(item => new FeedViewPostsSlice([item]))
}
}
export class FeedTuner {
@@ -86,22 +139,32 @@ export class FeedTuner {
tune(
feed: FeedViewPost[],
tunerFns: FeedTunerFn[] = [],
{dryRun, maintainOrder}: {dryRun: boolean; maintainOrder: boolean} = {
dryRun: false,
maintainOrder: false,
},
): FeedViewPostsSlice[] {
let slices: FeedViewPostsSlice[] = []
// arrange the posts into thread slices
for (let i = feed.length - 1; i >= 0; i--) {
const item = feed[i]
if (maintainOrder) {
slices = feed.map(item => new FeedViewPostsSlice([item]))
} else {
// arrange the posts into thread slices
for (let i = feed.length - 1; i >= 0; i--) {
const item = feed[i]
const selfReplyUri = getSelfReplyUri(item)
if (selfReplyUri) {
const parent = slices.find(item2 => item2.isNextInThread(selfReplyUri))
if (parent) {
parent.insert(item)
continue
const selfReplyUri = getSelfReplyUri(item)
if (selfReplyUri) {
const parent = slices.find(item2 =>
item2.isNextInThread(selfReplyUri),
)
if (parent) {
parent.insert(item)
continue
}
}
slices.unshift(new FeedViewPostsSlice([item]))
}
slices.unshift(new FeedViewPostsSlice([item]))
}
// run the custom tuners
@@ -123,28 +186,60 @@ export class FeedTuner {
// turn non-threads with reply parents into threads
for (const slice of slices) {
if (!slice.isThread && !slice.items[0].reason && slice.items[0].reply) {
const reply = slice.items[0].reply
if (
AppBskyFeedDefs.isPostView(reply.parent) &&
!this.seenUris.has(reply.parent.uri) &&
!soonToBeSeenUris.has(reply.parent.uri)
) {
const uri = reply.parent.uri
slice.flattenReplyParent()
soonToBeSeenUris.add(uri)
}
}
}
if (!dryRun) {
for (const slice of slices) {
for (const item of slice.items) {
this.seenUris.add(item.post.uri)
}
}
}
return slices
}
static removeReplies(tuner: FeedTuner, slices: FeedViewPostsSlice[]) {
for (let i = slices.length - 1; i >= 0; i--) {
if (slices[i].isReply) {
slices.splice(i, 1)
}
}
return slices
}
static removeReposts(tuner: FeedTuner, slices: FeedViewPostsSlice[]) {
for (let i = slices.length - 1; i >= 0; i--) {
const reason = slices[i].rootItem.reason
if (AppBskyFeedDefs.isReasonRepost(reason)) {
slices.splice(i, 1)
}
}
return slices
}
static removeQuotePosts(tuner: FeedTuner, slices: FeedViewPostsSlice[]) {
for (let i = slices.length - 1; i >= 0; i--) {
const embed = slices[i].rootItem.post.embed
if (
!slice.isThread &&
!slice.items[0].reason &&
slice.items[0].reply?.parent &&
!this.seenUris.has(slice.items[0].reply?.parent.uri) &&
!soonToBeSeenUris.has(slice.items[0].reply?.parent.uri)
AppBskyEmbedRecord.isView(embed) ||
AppBskyEmbedRecordWithMedia.isView(embed)
) {
const uri = slice.items[0].reply?.parent.uri
slice.flattenReplyParent()
soonToBeSeenUris.add(uri)
slices.splice(i, 1)
}
}
// sort by slice roots' timestamps
slices.sort((a, b) => b.ts.localeCompare(a.ts))
for (const slice of slices) {
for (const item of slice.items) {
this.seenUris.add(item.post.uri)
}
}
return slices
}
@@ -170,61 +265,87 @@ export class FeedTuner {
return slices
}
static likedRepliesOnly(
tuner: FeedTuner,
slices: FeedViewPostsSlice[],
): FeedViewPostsSlice[] {
// remove any replies without at least 2 likes
for (let i = slices.length - 1; i >= 0; i--) {
if (slices[i].isFullThread || !slices[i].rootItem.reply) {
continue
}
const item = slices[i].rootItem
const isRepost = Boolean(item.reason)
if (!isRepost && (item.post.likeCount || 0) < 2) {
slices.splice(i, 1)
}
}
return slices
}
static preferredLangOnly(langsCode2: string[]) {
const langsCode3 = langsCode2.map(l => LANGUAGES_MAP_CODE2[l]?.code3 || l)
static thresholdRepliesOnly({
userDid,
minLikes,
followedOnly,
}: {
userDid: string
minLikes: number
followedOnly: boolean
}) {
return (
tuner: FeedTuner,
slices: FeedViewPostsSlice[],
): FeedViewPostsSlice[] => {
const origSlices = slices.concat()
// remove any replies without at least minLikes likes
for (let i = slices.length - 1; i >= 0; i--) {
const slice = slices[i]
if (slice.isFullThread || !slice.isReply) {
continue
}
const item = slice.rootItem
const isRepost = Boolean(item.reason)
if (isRepost) {
continue
}
if ((item.post.likeCount || 0) < minLikes) {
slices.splice(i, 1)
} else if (followedOnly && !slice.isFollowingAllAuthors(userDid)) {
slices.splice(i, 1)
}
}
return slices
}
}
/**
* This function filters a list of FeedViewPostsSlice items based on whether they contain text in a
* preferred language.
* @param {string[]} preferredLangsCode2 - An array of preferred language codes in ISO 639-1 or ISO 639-2 format.
* @returns A function that takes in a `FeedTuner` and an array of `FeedViewPostsSlice` objects and
* returns an array of `FeedViewPostsSlice` objects.
*/
static preferredLangOnly(preferredLangsCode2: string[]) {
return (
tuner: FeedTuner,
slices: FeedViewPostsSlice[],
): FeedViewPostsSlice[] => {
// early return if no languages have been specified
if (!preferredLangsCode2.length || preferredLangsCode2.length === 0) {
return slices
}
for (let i = slices.length - 1; i >= 0; i--) {
let hasPreferredLang = false
for (const item of slices[i].items) {
if (
hasProp(item.post.record, 'text') &&
typeof item.post.record.text === 'string'
) {
const res = lande(item.post.record.text)
const contentLangCode3 = res[0][0]
if (langsCode3.includes(contentLangCode3)) {
hasPreferredLang = true
break
}
if (isPostInLanguage(item.post, preferredLangsCode2)) {
hasPreferredLang = true
break
}
}
// if item does not fit preferred language, remove it
if (!hasPreferredLang) {
slices.splice(i, 1)
}
}
if (slices.length) {
return slices
}
// fallback: give everything if the language filter left nothing
return origSlices
return slices
}
}
}
function getSelfReplyUri(item: FeedViewPost): string | undefined {
return item.reply?.parent.author.did === item.post.author.did
? item.reply?.parent.uri
: undefined
if (item.reply) {
if (
AppBskyFeedDefs.isPostView(item.reply.parent) &&
!AppBskyFeedDefs.isReasonRepost(item.reason) // don't thread reposted self-replies
) {
return item.reply.parent.author.did === item.post.author.did
? item.reply.parent.uri
: undefined
}
}
return undefined
}

Some files were not shown because too many files have changed in this diff Show More