diff --git a/.env.development b/.env.development
deleted file mode 100644
index d0f2796769..0000000000
--- a/.env.development
+++ /dev/null
@@ -1 +0,0 @@
-REACT_APP_BUILD = 'staging'
diff --git a/.env.production b/.env.production
deleted file mode 100644
index d0f2796769..0000000000
--- a/.env.production
+++ /dev/null
@@ -1 +0,0 @@
-REACT_APP_BUILD = 'staging'
diff --git a/src/env.native.ts b/src/env.native.ts
deleted file mode 100644
index a2ec3a4dce..0000000000
--- a/src/env.native.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-// @ts-ignore types not available -prf
-import {REACT_APP_BUILD} from '@env'
-
-if (typeof REACT_APP_BUILD !== 'string') {
- throw new Error('ENV: No env provided')
-}
-if (!['dev', 'staging', 'prod'].includes(REACT_APP_BUILD)) {
- throw new Error(
- `ENV: Env must be "dev", "staging", or "prod," got "${REACT_APP_BUILD}"`,
- )
-}
-
-export const BUILD = REACT_APP_BUILD
diff --git a/src/env.ts b/src/env.ts
deleted file mode 100644
index a379e435f2..0000000000
--- a/src/env.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-if (typeof process.env.REACT_APP_BUILD !== 'string') {
- throw new Error('ENV: No env provided')
-}
-if (!['dev', 'staging', 'prod'].includes(process.env.REACT_APP_BUILD)) {
- throw new Error(
- `ENV: Env must be "dev", "staging", or "prod," got "${process.env.REACT_APP_BUILD}"`,
- )
-}
-
-export const BUILD = process.env.REACT_APP_BUILD
diff --git a/src/state/index.ts b/src/state/index.ts
index a886e7611f..b16b516485 100644
--- a/src/state/index.ts
+++ b/src/state/index.ts
@@ -3,14 +3,12 @@ import {sessionClient as AtpApi} from '../third-party/api'
import {RootStoreModel} from './models/root-store'
import * as libapi from './lib/api'
import * as storage from './lib/storage'
-import {BUILD} from '../env'
-export const DEFAULT_SERVICE =
- BUILD === 'prod'
- ? 'http://localhost:2583' // TODO
- : BUILD === 'staging'
- ? 'https://pds.staging.bsky.dev' // TODO
- : 'http://localhost:2583'
+export const IS_PROD_BUILD = true
+export const LOCAL_DEV_SERVICE = 'http://localhost:2583'
+export const STAGING_SERVICE = 'https://pds.staging.bsky.dev'
+export const PROD_SERVICE = 'https://plc.bsky.social'
+export const DEFAULT_SERVICE = IS_PROD_BUILD ? PROD_SERVICE : LOCAL_DEV_SERVICE
const ROOT_STATE_STORAGE_KEY = 'root'
const STATE_FETCH_INTERVAL = 15e3
diff --git a/src/state/models/shell-ui.ts b/src/state/models/shell-ui.ts
index cc884f1c36..73b1bd56e7 100644
--- a/src/state/models/shell-ui.ts
+++ b/src/state/models/shell-ui.ts
@@ -66,6 +66,17 @@ export class InviteToSceneModel {
}
}
+export class ServerInputModel {
+ name = 'server-input'
+
+ constructor(
+ public initialService: string,
+ public onSelect: (url: string) => void,
+ ) {
+ makeAutoObservable(this)
+ }
+}
+
export interface ComposerOpts {
replyTo?: Post.PostRef
onPost?: () => void
@@ -79,6 +90,7 @@ export class ShellUiModel {
| SharePostModel
| EditProfileModel
| CreateSceneModel
+ | ServerInputModel
| undefined
isComposerActive = false
composerOpts: ComposerOpts | undefined
@@ -93,7 +105,8 @@ export class ShellUiModel {
| ConfirmModel
| SharePostModel
| EditProfileModel
- | CreateSceneModel,
+ | CreateSceneModel
+ | ServerInputModel,
) {
this.isModalActive = true
this.activeModal = modal
diff --git a/src/view/com/modals/Modal.tsx b/src/view/com/modals/Modal.tsx
index f2c61a6ae7..210cdc41f8 100644
--- a/src/view/com/modals/Modal.tsx
+++ b/src/view/com/modals/Modal.tsx
@@ -13,6 +13,7 @@ import * as SharePostModal from './SharePost.native'
import * as EditProfileModal from './EditProfile'
import * as CreateSceneModal from './CreateScene'
import * as InviteToSceneModal from './InviteToScene'
+import * as ServerInputModal from './ServerInput'
const CLOSED_SNAPPOINTS = ['10%']
@@ -77,6 +78,13 @@ export const Modal = observer(function Modal() {
{...(store.shell.activeModal as models.InviteToSceneModel)}
/>
)
+ } else if (store.shell.activeModal?.name === 'server-input') {
+ snapPoints = ServerInputModal.snapPoints
+ element = (
+
+ )
} else {
element =
}
diff --git a/src/view/com/modals/ServerInput.tsx b/src/view/com/modals/ServerInput.tsx
new file mode 100644
index 0000000000..1f3cc90f93
--- /dev/null
+++ b/src/view/com/modals/ServerInput.tsx
@@ -0,0 +1,140 @@
+import React, {useState} from 'react'
+import Toast from '../util/Toast'
+import {StyleSheet, Text, TextInput, TouchableOpacity, View} from 'react-native'
+import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
+import LinearGradient from 'react-native-linear-gradient'
+import {ErrorMessage} from '../util/ErrorMessage'
+import {useStores} from '../../../state'
+import {ProfileViewModel} from '../../../state/models/profile-view'
+import {s, colors, gradients} from '../../lib/styles'
+import {enforceLen, MAX_DISPLAY_NAME, MAX_DESCRIPTION} from '../../lib/strings'
+import {
+ IS_PROD_BUILD,
+ LOCAL_DEV_SERVICE,
+ STAGING_SERVICE,
+ PROD_SERVICE,
+} from '../../../state/index'
+
+export const snapPoints = ['80%']
+
+export function Component({
+ initialService,
+ onSelect,
+}: {
+ initialService: string
+ onSelect: (url: string) => void
+}) {
+ const store = useStores()
+ const [customUrl, setCustomUrl] = useState('')
+
+ const doSelect = (url: string) => {
+ if (!url.startsWith('http://') && !url.startsWith('https://')) {
+ url = `https://${url}`
+ }
+ store.shell.closeModal()
+ onSelect(url)
+ }
+
+ return (
+
+ Choose Service
+
+
+ {!IS_PROD_BUILD ? (
+ <>
+ doSelect(LOCAL_DEV_SERVICE)}>
+ Local dev server
+
+
+ doSelect(STAGING_SERVICE)}>
+ Staging
+
+
+ >
+ ) : undefined}
+ doSelect(PROD_SERVICE)}>
+ Bluesky.Social
+
+
+
+
+ Other service
+
+
+ doSelect(customUrl)}>
+
+
+
+
+
+
+ )
+}
+
+const styles = StyleSheet.create({
+ inner: {
+ padding: 14,
+ },
+ group: {
+ marginBottom: 20,
+ },
+ label: {
+ fontWeight: 'bold',
+ paddingHorizontal: 4,
+ paddingBottom: 4,
+ },
+ textInput: {
+ flex: 1,
+ borderWidth: 1,
+ borderColor: colors.gray3,
+ borderTopLeftRadius: 6,
+ borderBottomLeftRadius: 6,
+ paddingHorizontal: 14,
+ paddingVertical: 12,
+ fontSize: 16,
+ },
+ textInputBtn: {
+ borderWidth: 1,
+ borderLeftWidth: 0,
+ borderColor: colors.gray3,
+ borderTopRightRadius: 6,
+ borderBottomRightRadius: 6,
+ paddingHorizontal: 14,
+ paddingVertical: 10,
+ },
+ btn: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ backgroundColor: colors.blue3,
+ borderRadius: 6,
+ paddingHorizontal: 14,
+ paddingVertical: 10,
+ marginBottom: 6,
+ },
+ btnText: {
+ flex: 1,
+ fontSize: 18,
+ fontWeight: '500',
+ color: colors.white,
+ },
+})
diff --git a/src/view/index.ts b/src/view/index.ts
index 341051d4e0..78361e75b4 100644
--- a/src/view/index.ts
+++ b/src/view/index.ts
@@ -5,6 +5,7 @@ import {faAngleDown} from '@fortawesome/free-solid-svg-icons/faAngleDown'
import {faAngleLeft} from '@fortawesome/free-solid-svg-icons/faAngleLeft'
import {faAngleRight} from '@fortawesome/free-solid-svg-icons/faAngleRight'
import {faArrowLeft} from '@fortawesome/free-solid-svg-icons/faArrowLeft'
+import {faArrowRight} from '@fortawesome/free-solid-svg-icons/faArrowRight'
import {faArrowRightFromBracket} from '@fortawesome/free-solid-svg-icons'
import {faArrowUpFromBracket} from '@fortawesome/free-solid-svg-icons/faArrowUpFromBracket'
import {faArrowUpRightFromSquare} from '@fortawesome/free-solid-svg-icons/faArrowUpRightFromSquare'
@@ -35,6 +36,7 @@ import {faLock} from '@fortawesome/free-solid-svg-icons/faLock'
import {faMagnifyingGlass} from '@fortawesome/free-solid-svg-icons/faMagnifyingGlass'
import {faMessage} from '@fortawesome/free-regular-svg-icons/faMessage'
import {faNoteSticky} from '@fortawesome/free-solid-svg-icons/faNoteSticky'
+import {faPen} from '@fortawesome/free-solid-svg-icons/faPen'
import {faPenNib} from '@fortawesome/free-solid-svg-icons/faPenNib'
import {faPenToSquare} from '@fortawesome/free-solid-svg-icons/faPenToSquare'
import {faPlus} from '@fortawesome/free-solid-svg-icons/faPlus'
@@ -59,6 +61,7 @@ export function setup() {
faAngleLeft,
faAngleRight,
faArrowLeft,
+ faArrowRight,
faArrowRightFromBracket,
faArrowUpFromBracket,
faArrowUpRightFromSquare,
@@ -89,6 +92,7 @@ export function setup() {
faMagnifyingGlass,
faMessage,
faNoteSticky,
+ faPen,
faPenNib,
faPenToSquare,
faPlus,
diff --git a/src/view/lib/strings.ts b/src/view/lib/strings.ts
index 214bb51d63..05b23331f3 100644
--- a/src/view/lib/strings.ts
+++ b/src/view/lib/strings.ts
@@ -1,5 +1,6 @@
import {AtUri} from '../../third-party/uri'
import {Entity} from '../../third-party/api/src/client/types/app/bsky/feed/post'
+import {PROD_SERVICE} from '../../state'
export const MAX_DISPLAY_NAME = 64
export const MAX_DESCRIPTION = 256
@@ -106,3 +107,15 @@ export function cleanError(str: string): string {
}
return str
}
+
+export function toNiceDomain(url: string): string {
+ try {
+ const urlp = new URL(url)
+ if (`https://${urlp.host}` === PROD_SERVICE) {
+ return 'Bluesky.Social'
+ }
+ return urlp.host
+ } catch (e) {
+ return url
+ }
+}
diff --git a/src/view/screens/Home.tsx b/src/view/screens/Home.tsx
index 036f7d1487..04ce2d0cb9 100644
--- a/src/view/screens/Home.tsx
+++ b/src/view/screens/Home.tsx
@@ -8,7 +8,6 @@ import {useStores} from '../../state'
import {FeedModel} from '../../state/models/feed-view'
import {ScreenParams} from '../routes'
import {s} from '../lib/styles'
-import {BUILD} from '../../env'
export const Home = observer(function Home({
visible,
@@ -57,10 +56,7 @@ export const Home = observer(function Home({
return (
-
+
Bluesky
-
- [ private beta {BUILD !== 'prod' ? `- ${BUILD} ` : ''}]
-
+ [ private beta ]
@@ -112,6 +111,7 @@ const SigninOrCreateAccount = ({
const Signin = ({onPressBack}: {onPressBack: () => void}) => {
const store = useStores()
const [isProcessing, setIsProcessing] = useState(false)
+ const [serviceUrl, setServiceUrl] = useState(DEFAULT_SERVICE)
const [serviceDescription, setServiceDescription] = useState<
ServiceDescription | undefined
>(undefined)
@@ -121,10 +121,9 @@ const Signin = ({onPressBack}: {onPressBack: () => void}) => {
useEffect(() => {
let aborted = false
- if (serviceDescription || error) {
- return
- }
- store.session.describeService(DEFAULT_SERVICE).then(
+ setError('')
+ console.log('Fetching service description', serviceUrl)
+ store.session.describeService(serviceUrl).then(
desc => {
if (aborted) return
setServiceDescription(desc)
@@ -140,7 +139,11 @@ const Signin = ({onPressBack}: {onPressBack: () => void}) => {
return () => {
aborted = true
}
- }, [])
+ }, [serviceUrl])
+
+ const onPressSelectService = () => {
+ store.shell.openModal(new ServerInputModel(serviceUrl, setServiceUrl))
+ }
const onPressNext = async () => {
setError('')
@@ -168,7 +171,7 @@ const Signin = ({onPressBack}: {onPressBack: () => void}) => {
}
await store.session.login({
- service: DEFAULT_SERVICE,
+ service: serviceUrl,
handle: fullHandle,
password,
})
@@ -194,9 +197,14 @@ const Signin = ({onPressBack}: {onPressBack: () => void}) => {
-
- Sign in
-
+
+
+ Sign in to {toNiceDomain(serviceUrl)}
+
+
+
{error ? (
@@ -256,6 +264,7 @@ const Signin = ({onPressBack}: {onPressBack: () => void}) => {
const CreateAccount = ({onPressBack}: {onPressBack: () => void}) => {
const store = useStores()
const [isProcessing, setIsProcessing] = useState(false)
+ const [serviceUrl, setServiceUrl] = useState(DEFAULT_SERVICE)
const [error, setError] = useState('')
const [serviceDescription, setServiceDescription] = useState<
ServiceDescription | undefined
@@ -268,10 +277,9 @@ const CreateAccount = ({onPressBack}: {onPressBack: () => void}) => {
useEffect(() => {
let aborted = false
- if (serviceDescription || error) {
- return
- }
- store.session.describeService(DEFAULT_SERVICE).then(
+ setError('')
+ console.log('Fetching service description', serviceUrl)
+ store.session.describeService(serviceUrl).then(
desc => {
if (aborted) return
setServiceDescription(desc)
@@ -288,7 +296,11 @@ const CreateAccount = ({onPressBack}: {onPressBack: () => void}) => {
return () => {
aborted = true
}
- }, [])
+ }, [serviceUrl])
+
+ const onPressSelectService = () => {
+ store.shell.openModal(new ServerInputModel(serviceUrl, setServiceUrl))
+ }
const onPressNext = async () => {
if (!email) {
@@ -307,7 +319,7 @@ const CreateAccount = ({onPressBack}: {onPressBack: () => void}) => {
setIsProcessing(true)
try {
await store.session.createAccount({
- service: DEFAULT_SERVICE,
+ service: serviceUrl,
email,
handle: createFullHandle(handle, userDomain),
password,
@@ -346,136 +358,164 @@ const CreateAccount = ({onPressBack}: {onPressBack: () => void}) => {
)
return (
-
-
-
-
- {serviceDescription ? (
- <>
- {error ? (
-
-
-
+
+
+
+
+
+ {serviceDescription ? (
+ <>
+ {error ? (
+
+
+
+
+
+ {error}
+
-
- {error}
+ ) : undefined}
+
+
+
+ Create a new account
+
-
- ) : undefined}
-
-
- Create a new account
-
- {serviceDescription?.inviteCodeRequired ? (
+
+
+
+
+ {toNiceDomain(serviceUrl)}
+
+
+
+
+ {serviceDescription?.inviteCodeRequired ? (
+
+
+
+
+ ) : undefined}
- ) : undefined}
-
-
-
-
-
-
-
-
-
-
-
- Choose your username
-
-
-
- setHandle(makeValidHandle(v))}
- editable={!isProcessing}
- />
-
- {serviceDescription.availableUserDomains.length > 1 && (
-
- ({
- label: `.${d}`,
- value: d,
- }))}
- onChange={itemValue => setUserDomain(itemValue)}
- enabled={!isProcessing}
+
+
- )}
-
-
- Your full username will be{' '}
-
- @{createFullHandle(handle, userDomain)}
-
-
-
-
-
- Back
-
-
-
- {isProcessing ? (
-
- ) : (
- Next
+
+
+
+ Choose your username
+
+
+
+
+ setHandle(makeValidHandle(v))}
+ editable={!isProcessing}
+ />
+
+ {serviceDescription.availableUserDomains.length > 1 && (
+
+
+ ({
+ label: `.${d}`,
+ value: d,
+ }))}
+ onChange={itemValue => setUserDomain(itemValue)}
+ enabled={!isProcessing}
+ />
+
)}
-
-
- >
- ) : (
-
- )}
-
+
+
+ Your full username will be{' '}
+
+ @{createFullHandle(handle, userDomain)}
+
+
+
+
+
+
+ Back
+
+
+
+ {isProcessing ? (
+
+ ) : (
+ Next
+ )}
+
+
+ >
+ ) : (
+
+ )}
+
+
)
}
@@ -577,9 +617,15 @@ const styles = StyleSheet.create({
backgroundColor: colors.blue3,
},
groupTitle: {
+ flexDirection: 'row',
+ alignItems: 'center',
paddingVertical: 8,
paddingHorizontal: 12,
},
+ groupTitleIcon: {
+ color: colors.white,
+ marginHorizontal: 6,
+ },
groupContent: {
borderTopWidth: 1,
borderTopColor: colors.blue1,
@@ -600,6 +646,22 @@ const styles = StyleSheet.create({
fontSize: 18,
borderRadius: 10,
},
+ textBtn: {
+ flexDirection: 'row',
+ flex: 1,
+ alignItems: 'center',
+ },
+ textBtnLabel: {
+ flex: 1,
+ color: colors.white,
+ paddingVertical: 10,
+ paddingHorizontal: 12,
+ fontSize: 18,
+ },
+ textBtnIcon: {
+ color: colors.white,
+ marginHorizontal: 12,
+ },
picker: {
flex: 1,
width: '100%',
diff --git a/src/view/shell/mobile/index.tsx b/src/view/shell/mobile/index.tsx
index 712d6dc23d..96390e9b83 100644
--- a/src/view/shell/mobile/index.tsx
+++ b/src/view/shell/mobile/index.tsx
@@ -170,6 +170,7 @@ export const MobileShell: React.FC = observer(() => {
+
)
}
@@ -294,6 +295,7 @@ function constructScreenRenderDesc(nav: NavigationModel): {
const styles = StyleSheet.create({
outerContainer: {
height: '100%',
+ flex: 1,
},
innerContainer: {
flex: 1,
diff --git a/todos.txt b/todos.txt
index e9879bf4c0..da966aa245 100644
--- a/todos.txt
+++ b/todos.txt
@@ -6,6 +6,7 @@ Paul's todo list
- Cursor behaviors on all views
- Update swipe behaviors: edge always goes back, leftmost always goes back, main connects to selector if present
- Onboarding flow
+ > Invite codes
- Confirm email
- Setup profile?
- Onboarding
@@ -23,7 +24,7 @@ Paul's todo list
- View on post
- Linking
- Web linking
- - App linking
+ > App linking
- Pagination
- Liked by
- Reposted by
@@ -33,7 +34,4 @@ Paul's todo list
- Bugs
- Auth token refresh seems broken
- Check that sub components arent reloading too much
- - Titles are getting screwed up (possibly swipe related)
- > Dont suggest self for follows
- > Double post on post?
- > Handle no displayname everywhere
\ No newline at end of file
+ - Titles are getting screwed up (possibly swipe related)
\ No newline at end of file