Resolve all remaining lint issues (#88)
* Rework 'navIdx' variables from number arrays to strings to avoid equality-check failures in react hooks * Resolve all remaining lint issues * Fix tests * Use node v18 in gh action testzio/stable
parent
3a90114f3a
commit
f36c956536
|
@ -25,6 +25,10 @@ jobs:
|
||||||
name: Run tests
|
name: Run tests
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
|
- name: Install node 18
|
||||||
|
uses: actions/setup-node@v3
|
||||||
|
with:
|
||||||
|
node-version: 18
|
||||||
- name: Check out Git repository
|
- name: Check out Git repository
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
- name: Yarn install
|
- name: Yarn install
|
||||||
|
|
|
@ -6,7 +6,7 @@ describe('NavigationModel', () => {
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
model = new NavigationModel()
|
model = new NavigationModel()
|
||||||
model.setTitle([0, 0], 'title')
|
model.setTitle('0-0', 'title')
|
||||||
})
|
})
|
||||||
|
|
||||||
afterAll(() => {
|
afterAll(() => {
|
||||||
|
@ -44,7 +44,7 @@ describe('NavigationModel', () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should call the isCurrentScreen method', () => {
|
it('should call the isCurrentScreen method', () => {
|
||||||
expect(model.isCurrentScreen(11, 0)).toEqual(false)
|
expect(model.isCurrentScreen('11', 0)).toEqual(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should call the tab getter', () => {
|
it('should call the tab getter', () => {
|
||||||
|
|
|
@ -41,7 +41,6 @@ function* dateGen() {
|
||||||
yield new Date(start).toISOString()
|
yield new Date(start).toISOString()
|
||||||
start += 1e3
|
start += 1e3
|
||||||
}
|
}
|
||||||
return ''
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function createServer(): Promise<TestPDS> {
|
export async function createServer(): Promise<TestPDS> {
|
||||||
|
|
|
@ -4,8 +4,6 @@
|
||||||
*
|
*
|
||||||
* @format
|
* @format
|
||||||
*/
|
*/
|
||||||
const metroResolver = require('metro-resolver')
|
|
||||||
const path = require('path')
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
transformer: {
|
transformer: {
|
||||||
|
|
|
@ -10,6 +10,7 @@ import {ThemeProvider} from './view/lib/ThemeContext'
|
||||||
import * as view from './view/index'
|
import * as view from './view/index'
|
||||||
import {RootStoreModel, setupState, RootStoreProvider} from './state'
|
import {RootStoreModel, setupState, RootStoreProvider} from './state'
|
||||||
import {MobileShell} from './view/shell/mobile'
|
import {MobileShell} from './view/shell/mobile'
|
||||||
|
import {s} from './view/lib/styles'
|
||||||
|
|
||||||
const App = observer(() => {
|
const App = observer(() => {
|
||||||
const [rootStore, setRootStore] = useState<RootStoreModel | undefined>(
|
const [rootStore, setRootStore] = useState<RootStoreModel | undefined>(
|
||||||
|
@ -39,7 +40,7 @@ const App = observer(() => {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<GestureHandlerRootView style={{flex: 1}}>
|
<GestureHandlerRootView style={s.h100pct}>
|
||||||
<RootSiblingParent>
|
<RootSiblingParent>
|
||||||
<RootStoreProvider value={rootStore}>
|
<RootStoreProvider value={rootStore}>
|
||||||
<ThemeProvider theme={rootStore.shell.darkMode ? 'dark' : 'light'}>
|
<ThemeProvider theme={rootStore.shell.darkMode ? 'dark' : 'light'}>
|
||||||
|
|
|
@ -78,7 +78,7 @@ export function extractEntities(
|
||||||
let ents: Entity[] = []
|
let ents: Entity[] = []
|
||||||
{
|
{
|
||||||
// mentions
|
// mentions
|
||||||
const re = /(^|\s|\()(@)([a-zA-Z0-9\.-]+)(\b)/g
|
const re = /(^|\s|\()(@)([a-zA-Z0-9.-]+)(\b)/g
|
||||||
while ((match = re.exec(text))) {
|
while ((match = re.exec(text))) {
|
||||||
if (knownHandles && !knownHandles.has(match[3])) {
|
if (knownHandles && !knownHandles.has(match[3])) {
|
||||||
continue // not a known handle
|
continue // not a known handle
|
||||||
|
@ -133,7 +133,7 @@ interface DetectedLink {
|
||||||
type DetectedLinkable = string | DetectedLink
|
type DetectedLinkable = string | DetectedLink
|
||||||
export function detectLinkables(text: string): DetectedLinkable[] {
|
export function detectLinkables(text: string): DetectedLinkable[] {
|
||||||
const re =
|
const re =
|
||||||
/((^|\s|\()@[a-z0-9\.-]*)|((^|\s|\()https?:\/\/[\S]+)|((^|\s|\()(?<domain>[a-z][a-z0-9]*(\.[a-z0-9]+)+)[\S]*)/gi
|
/((^|\s|\()@[a-z0-9.-]*)|((^|\s|\()https?:\/\/[\S]+)|((^|\s|\()(?<domain>[a-z][a-z0-9]*(\.[a-z0-9]+)+)[\S]*)/gi
|
||||||
const segments = []
|
const segments = []
|
||||||
let match
|
let match
|
||||||
let start = 0
|
let start = 0
|
||||||
|
@ -154,7 +154,6 @@ export function detectLinkables(text: string): DetectedLinkable[] {
|
||||||
matchValue = matchValue.slice(1)
|
matchValue = matchValue.slice(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
|
||||||
// strip ending puncuation
|
// strip ending puncuation
|
||||||
if (/[.,;!?]$/.test(matchValue)) {
|
if (/[.,;!?]$/.test(matchValue)) {
|
||||||
matchValue = matchValue.slice(0, -1)
|
matchValue = matchValue.slice(0, -1)
|
||||||
|
@ -162,7 +161,6 @@ export function detectLinkables(text: string): DetectedLinkable[] {
|
||||||
if (/[)]$/.test(matchValue) && !matchValue.includes('(')) {
|
if (/[)]$/.test(matchValue) && !matchValue.includes('(')) {
|
||||||
matchValue = matchValue.slice(0, -1)
|
matchValue = matchValue.slice(0, -1)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (start !== matchIndex) {
|
if (start !== matchIndex) {
|
||||||
segments.push(text.slice(start, matchIndex))
|
segments.push(text.slice(start, matchIndex))
|
||||||
|
@ -185,8 +183,8 @@ export function makeValidHandle(str: string): string {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createFullHandle(name: string, domain: string): string {
|
export function createFullHandle(name: string, domain: string): string {
|
||||||
name = (name || '').replace(/[\.]+$/, '')
|
name = (name || '').replace(/[.]+$/, '')
|
||||||
domain = (domain || '').replace(/^[\.]+/, '')
|
domain = (domain || '').replace(/^[.]+/, '')
|
||||||
return `${name}.${domain}`
|
return `${name}.${domain}`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ import {TABS_ENABLED} from '../../build-flags'
|
||||||
|
|
||||||
let __id = 0
|
let __id = 0
|
||||||
function genId() {
|
function genId() {
|
||||||
return ++__id
|
return String(++__id)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE
|
// NOTE
|
||||||
|
@ -24,10 +24,10 @@ interface HistoryItem {
|
||||||
url: string
|
url: string
|
||||||
ts: number
|
ts: number
|
||||||
title?: string
|
title?: string
|
||||||
id: number
|
id: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type HistoryPtr = [number, number]
|
export type HistoryPtr = string // `{tabId}-{historyId}`
|
||||||
|
|
||||||
export class NavigationTabModel {
|
export class NavigationTabModel {
|
||||||
id = genId()
|
id = genId()
|
||||||
|
@ -151,7 +151,7 @@ export class NavigationTabModel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setTitle(id: number, title: string) {
|
setTitle(id: string, title: string) {
|
||||||
this.history = this.history.map(h => {
|
this.history = this.history.map(h => {
|
||||||
if (h.id === id) {
|
if (h.id === id) {
|
||||||
return {...h, title}
|
return {...h, title}
|
||||||
|
@ -174,7 +174,7 @@ export class NavigationTabModel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
hydrate(v: unknown) {
|
hydrate(_v: unknown) {
|
||||||
// TODO fixme
|
// TODO fixme
|
||||||
// if (isObj(v)) {
|
// if (isObj(v)) {
|
||||||
// if (hasProp(v, 'history') && Array.isArray(v.history)) {
|
// if (hasProp(v, 'history') && Array.isArray(v.history)) {
|
||||||
|
@ -241,7 +241,7 @@ export class NavigationModel {
|
||||||
return this.tabs.length
|
return this.tabs.length
|
||||||
}
|
}
|
||||||
|
|
||||||
isCurrentScreen(tabId: number, index: number) {
|
isCurrentScreen(tabId: string, index: number) {
|
||||||
return this.tab.id === tabId && this.tab.index === index
|
return this.tab.id === tabId && this.tab.index === index
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -257,7 +257,8 @@ export class NavigationModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
setTitle(ptr: HistoryPtr, title: string) {
|
setTitle(ptr: HistoryPtr, title: string) {
|
||||||
this.tabs.find(t => t.id === ptr[0])?.setTitle(ptr[1], title)
|
const [tid, hid] = ptr.split('-')
|
||||||
|
this.tabs.find(t => t.id === tid)?.setTitle(hid, title)
|
||||||
}
|
}
|
||||||
|
|
||||||
handleLink(url: string) {
|
handleLink(url: string) {
|
||||||
|
@ -338,7 +339,7 @@ export class NavigationModel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
hydrate(v: unknown) {
|
hydrate(_v: unknown) {
|
||||||
// TODO fixme
|
// TODO fixme
|
||||||
this.clear()
|
this.clear()
|
||||||
/*if (isObj(v)) {
|
/*if (isObj(v)) {
|
||||||
|
|
|
@ -297,7 +297,7 @@ export const ComposePost = observer(function ComposePost({
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}, [text, pal.link])
|
}, [text, pal.link, pal.text])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<KeyboardAvoidingView
|
<KeyboardAvoidingView
|
||||||
|
@ -393,7 +393,7 @@ export const ComposePost = observer(function ComposePost({
|
||||||
ref={textInput}
|
ref={textInput}
|
||||||
multiline
|
multiline
|
||||||
scrollEnabled
|
scrollEnabled
|
||||||
onChangeText={(text: string) => onChangeText(text)}
|
onChangeText={(str: string) => onChangeText(str)}
|
||||||
onPaste={onPaste}
|
onPaste={onPaste}
|
||||||
placeholder={selectTextInputPlaceholder}
|
placeholder={selectTextInputPlaceholder}
|
||||||
placeholderTextColor={pal.colors.textLight}
|
placeholderTextColor={pal.colors.textLight}
|
||||||
|
@ -475,7 +475,7 @@ export const ComposePost = observer(function ComposePost({
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
const atPrefixRegex = /@([a-z0-9\.]*)$/i
|
const atPrefixRegex = /@([a-z0-9.]*)$/i
|
||||||
function extractTextAutocompletePrefix(text: string) {
|
function extractTextAutocompletePrefix(text: string) {
|
||||||
const match = atPrefixRegex.exec(text)
|
const match = atPrefixRegex.exec(text)
|
||||||
if (match) {
|
if (match) {
|
||||||
|
|
|
@ -39,7 +39,7 @@ export const SuggestedFollows = observer(
|
||||||
// Using default import (React.use...) instead of named import (use...) to be able to mock store's data in jest environment
|
// Using default import (React.use...) instead of named import (use...) to be able to mock store's data in jest environment
|
||||||
const view = React.useMemo<SuggestedActorsViewModel>(
|
const view = React.useMemo<SuggestedActorsViewModel>(
|
||||||
() => new SuggestedActorsViewModel(store),
|
() => new SuggestedActorsViewModel(store),
|
||||||
[],
|
[store],
|
||||||
)
|
)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -54,7 +54,7 @@ export const SuggestedFollows = observer(
|
||||||
if (!view.isLoading && !view.hasError && !view.hasContent) {
|
if (!view.isLoading && !view.hasError && !view.hasContent) {
|
||||||
onNoSuggestions?.()
|
onNoSuggestions?.()
|
||||||
}
|
}
|
||||||
}, [view, view.isLoading, view.hasError, view.hasContent])
|
}, [view, view.isLoading, view.hasError, view.hasContent, onNoSuggestions])
|
||||||
|
|
||||||
const onPressTryAgain = () =>
|
const onPressTryAgain = () =>
|
||||||
view
|
view
|
||||||
|
@ -128,7 +128,7 @@ export const SuggestedFollows = observer(
|
||||||
keyExtractor={item => item._reactKey}
|
keyExtractor={item => item._reactKey}
|
||||||
renderItem={renderItem}
|
renderItem={renderItem}
|
||||||
style={s.flex1}
|
style={s.flex1}
|
||||||
contentContainerStyle={{paddingBottom: 200}}
|
contentContainerStyle={s.contentContainer}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -207,12 +207,7 @@ const ChooseAccountForm = ({
|
||||||
style={[pal.borderDark, styles.group]}
|
style={[pal.borderDark, styles.group]}
|
||||||
onPress={() => onSelectAccount(undefined)}>
|
onPress={() => onSelectAccount(undefined)}>
|
||||||
<View style={[pal.borderDark, styles.groupContent, styles.noTopBorder]}>
|
<View style={[pal.borderDark, styles.groupContent, styles.noTopBorder]}>
|
||||||
<View style={s.p10}>
|
<Text style={[styles.accountText, styles.accountTextOther]}>
|
||||||
<View
|
|
||||||
style={[pal.btn, {width: 30, height: 30, borderRadius: 15}]}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
<Text style={styles.accountText}>
|
|
||||||
<Text type="lg" style={pal.text}>
|
<Text type="lg" style={pal.text}>
|
||||||
Other account
|
Other account
|
||||||
</Text>
|
</Text>
|
||||||
|
@ -556,7 +551,7 @@ const ForgotPasswordForm = ({
|
||||||
{!serviceDescription || isProcessing ? (
|
{!serviceDescription || isProcessing ? (
|
||||||
<ActivityIndicator />
|
<ActivityIndicator />
|
||||||
) : !email ? (
|
) : !email ? (
|
||||||
<Text type="xl-bold" style={[pal.link, s.pr5, {opacity: 0.5}]}>
|
<Text type="xl-bold" style={[pal.link, s.pr5, styles.dimmed]}>
|
||||||
Next
|
Next
|
||||||
</Text>
|
</Text>
|
||||||
) : (
|
) : (
|
||||||
|
@ -691,7 +686,7 @@ const SetNewPasswordForm = ({
|
||||||
{isProcessing ? (
|
{isProcessing ? (
|
||||||
<ActivityIndicator />
|
<ActivityIndicator />
|
||||||
) : !resetCode || !password ? (
|
) : !resetCode || !password ? (
|
||||||
<Text type="xl-bold" style={[pal.link, s.pr5, {opacity: 0.5}]}>
|
<Text type="xl-bold" style={[pal.link, s.pr5, styles.dimmed]}>
|
||||||
Next
|
Next
|
||||||
</Text>
|
</Text>
|
||||||
) : (
|
) : (
|
||||||
|
@ -810,6 +805,9 @@ const styles = StyleSheet.create({
|
||||||
alignItems: 'baseline',
|
alignItems: 'baseline',
|
||||||
paddingVertical: 10,
|
paddingVertical: 10,
|
||||||
},
|
},
|
||||||
|
accountTextOther: {
|
||||||
|
paddingLeft: 12,
|
||||||
|
},
|
||||||
error: {
|
error: {
|
||||||
backgroundColor: colors.red4,
|
backgroundColor: colors.red4,
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
|
@ -832,4 +830,5 @@ const styles = StyleSheet.create({
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
marginRight: 5,
|
marginRight: 5,
|
||||||
},
|
},
|
||||||
|
dimmed: {opacity: 0.5},
|
||||||
})
|
})
|
||||||
|
|
|
@ -121,7 +121,7 @@ export function Component({
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
{error !== '' && (
|
{error !== '' && (
|
||||||
<View style={{marginTop: 20}}>
|
<View style={styles.errorContainer}>
|
||||||
<ErrorMessage message={error} />
|
<ErrorMessage message={error} />
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
|
@ -231,4 +231,5 @@ const styles = StyleSheet.create({
|
||||||
marginBottom: 36,
|
marginBottom: 36,
|
||||||
marginHorizontal: -14,
|
marginHorizontal: -14,
|
||||||
},
|
},
|
||||||
|
errorContainer: {marginTop: 20},
|
||||||
})
|
})
|
||||||
|
|
|
@ -56,7 +56,7 @@ export function Component({onSelect}: {onSelect: (url: string) => void}) {
|
||||||
</View>
|
</View>
|
||||||
<View style={styles.group}>
|
<View style={styles.group}>
|
||||||
<Text style={styles.label}>Other service</Text>
|
<Text style={styles.label}>Other service</Text>
|
||||||
<View style={{flexDirection: 'row'}}>
|
<View style={s.flexRow}>
|
||||||
<BottomSheetTextInput
|
<BottomSheetTextInput
|
||||||
testID="customServerTextInput"
|
testID="customServerTextInput"
|
||||||
style={styles.textInput}
|
style={styles.textInput}
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {observer} from 'mobx-react-lite'
|
import {observer} from 'mobx-react-lite'
|
||||||
import {View, FlatList} from 'react-native'
|
import {FlatList, StyleSheet, View} from 'react-native'
|
||||||
import {NotificationsViewModel} from '../../../state/models/notifications-view'
|
import {NotificationsViewModel} from '../../../state/models/notifications-view'
|
||||||
import {FeedItem} from './FeedItem'
|
import {FeedItem} from './FeedItem'
|
||||||
import {NotificationFeedLoadingPlaceholder} from '../util/LoadingPlaceholder'
|
import {NotificationFeedLoadingPlaceholder} from '../util/LoadingPlaceholder'
|
||||||
import {ErrorMessage} from '../util/error/ErrorMessage'
|
import {ErrorMessage} from '../util/error/ErrorMessage'
|
||||||
import {EmptyState} from '../util/EmptyState'
|
import {EmptyState} from '../util/EmptyState'
|
||||||
import {OnScrollCb} from '../../lib/hooks/useOnMainScroll'
|
import {OnScrollCb} from '../../lib/hooks/useOnMainScroll'
|
||||||
|
import {s} from '../../lib/styles'
|
||||||
|
|
||||||
const EMPTY_FEED_ITEM = {_reactKey: '__empty__'}
|
const EMPTY_FEED_ITEM = {_reactKey: '__empty__'}
|
||||||
|
|
||||||
|
@ -29,7 +30,7 @@ export const Feed = observer(function Feed({
|
||||||
<EmptyState
|
<EmptyState
|
||||||
icon="bell"
|
icon="bell"
|
||||||
message="No notifications yet!"
|
message="No notifications yet!"
|
||||||
style={{paddingVertical: 40}}
|
style={styles.emptyState}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -58,14 +59,10 @@ export const Feed = observer(function Feed({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<View style={{flex: 1}}>
|
<View style={s.h100pct}>
|
||||||
{view.isLoading && !data && <NotificationFeedLoadingPlaceholder />}
|
{view.isLoading && !data && <NotificationFeedLoadingPlaceholder />}
|
||||||
{view.hasError && (
|
{view.hasError && (
|
||||||
<ErrorMessage
|
<ErrorMessage message={view.error} onPressTryAgain={onPressTryAgain} />
|
||||||
message={view.error}
|
|
||||||
style={{margin: 6}}
|
|
||||||
onPressTryAgain={onPressTryAgain}
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
{data && (
|
{data && (
|
||||||
<FlatList
|
<FlatList
|
||||||
|
@ -76,9 +73,13 @@ export const Feed = observer(function Feed({
|
||||||
onRefresh={onRefresh}
|
onRefresh={onRefresh}
|
||||||
onEndReached={onEndReached}
|
onEndReached={onEndReached}
|
||||||
onScroll={onScroll}
|
onScroll={onScroll}
|
||||||
contentContainerStyle={{paddingBottom: 200}}
|
contentContainerStyle={s.contentContainer}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
emptyState: {paddingVertical: 40},
|
||||||
|
})
|
||||||
|
|
|
@ -19,14 +19,13 @@ import {TABS_ENABLED} from '../../../build-flags'
|
||||||
const Intro = () => (
|
const Intro = () => (
|
||||||
<View style={styles.explainer}>
|
<View style={styles.explainer}>
|
||||||
<Text
|
<Text
|
||||||
style={[
|
style={[styles.explainerHeading, s.normal, styles.explainerHeadingIntro]}>
|
||||||
styles.explainerHeading,
|
Welcome to{' '}
|
||||||
s.normal,
|
<Text style={[s.bold, s.blue3, styles.explainerHeadingBrand]}>
|
||||||
{lineHeight: 60, paddingTop: 50, paddingBottom: 50},
|
Bluesky
|
||||||
]}>
|
|
||||||
Welcome to <Text style={[s.bold, s.blue3, {fontSize: 56}]}>Bluesky</Text>
|
|
||||||
</Text>
|
</Text>
|
||||||
<Text style={[styles.explainerDesc, {fontSize: 24}]}>
|
</Text>
|
||||||
|
<Text style={[styles.explainerDesc, styles.explainerDescIntro]}>
|
||||||
This is an early beta. Your feedback is appreciated!
|
This is an early beta. Your feedback is appreciated!
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
|
@ -161,11 +160,18 @@ const styles = StyleSheet.create({
|
||||||
textAlign: 'center',
|
textAlign: 'center',
|
||||||
marginBottom: 16,
|
marginBottom: 16,
|
||||||
},
|
},
|
||||||
|
explainerHeadingIntro: {
|
||||||
|
lineHeight: 60,
|
||||||
|
paddingTop: 50,
|
||||||
|
paddingBottom: 50,
|
||||||
|
},
|
||||||
|
explainerHeadingBrand: {fontSize: 56},
|
||||||
explainerDesc: {
|
explainerDesc: {
|
||||||
fontSize: 18,
|
fontSize: 18,
|
||||||
textAlign: 'center',
|
textAlign: 'center',
|
||||||
marginBottom: 16,
|
marginBottom: 16,
|
||||||
},
|
},
|
||||||
|
explainerDescIntro: {fontSize: 24},
|
||||||
explainerImg: {
|
explainerImg: {
|
||||||
resizeMode: 'contain',
|
resizeMode: 'contain',
|
||||||
maxWidth: '100%',
|
maxWidth: '100%',
|
||||||
|
|
|
@ -53,11 +53,7 @@ export const PostRepostedBy = observer(function PostRepostedBy({
|
||||||
if (view.hasError) {
|
if (view.hasError) {
|
||||||
return (
|
return (
|
||||||
<View>
|
<View>
|
||||||
<ErrorMessage
|
<ErrorMessage message={view.error} onPressTryAgain={onRefresh} />
|
||||||
message={view.error}
|
|
||||||
style={{margin: 6}}
|
|
||||||
onPressTryAgain={onRefresh}
|
|
||||||
/>
|
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import {
|
||||||
} from '../../../state/models/post-thread-view'
|
} from '../../../state/models/post-thread-view'
|
||||||
import {PostThreadItem} from './PostThreadItem'
|
import {PostThreadItem} from './PostThreadItem'
|
||||||
import {ErrorMessage} from '../util/error/ErrorMessage'
|
import {ErrorMessage} from '../util/error/ErrorMessage'
|
||||||
|
import {s} from '../../lib/styles'
|
||||||
|
|
||||||
export const PostThread = observer(function PostThread({
|
export const PostThread = observer(function PostThread({
|
||||||
uri,
|
uri,
|
||||||
|
@ -60,11 +61,7 @@ export const PostThread = observer(function PostThread({
|
||||||
if (view.hasError) {
|
if (view.hasError) {
|
||||||
return (
|
return (
|
||||||
<View>
|
<View>
|
||||||
<ErrorMessage
|
<ErrorMessage message={view.error} onPressTryAgain={onRefresh} />
|
||||||
message={view.error}
|
|
||||||
style={{margin: 6}}
|
|
||||||
onPressTryAgain={onRefresh}
|
|
||||||
/>
|
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -84,8 +81,8 @@ export const PostThread = observer(function PostThread({
|
||||||
onRefresh={onRefresh}
|
onRefresh={onRefresh}
|
||||||
onLayout={onLayout}
|
onLayout={onLayout}
|
||||||
onScrollToIndexFailed={onScrollToIndexFailed}
|
onScrollToIndexFailed={onScrollToIndexFailed}
|
||||||
style={{flex: 1}}
|
style={s.h100pct}
|
||||||
contentContainerStyle={{paddingBottom: 200}}
|
contentContainerStyle={s.contentContainer}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
|
@ -80,7 +80,7 @@ export const PostThreadItem = observer(function PostThreadItem({
|
||||||
.catch(e => store.log.error('Failed to toggle upvote', e))
|
.catch(e => store.log.error('Failed to toggle upvote', e))
|
||||||
}
|
}
|
||||||
const onCopyPostText = () => {
|
const onCopyPostText = () => {
|
||||||
Clipboard.setString(record.text)
|
Clipboard.setString(record?.text || '')
|
||||||
Toast.show('Copied to clipboard')
|
Toast.show('Copied to clipboard')
|
||||||
}
|
}
|
||||||
const onDeletePost = () => {
|
const onDeletePost = () => {
|
||||||
|
@ -131,8 +131,8 @@ export const PostThreadItem = observer(function PostThreadItem({
|
||||||
</Link>
|
</Link>
|
||||||
</View>
|
</View>
|
||||||
<View style={styles.layoutContent}>
|
<View style={styles.layoutContent}>
|
||||||
<View style={[styles.meta, {paddingTop: 5, paddingBottom: 0}]}>
|
<View style={[styles.meta, styles.metaExpandedLine1]}>
|
||||||
<View style={{flexDirection: 'row', alignItems: 'baseline'}}>
|
<View style={[s.flexRow, s.alignBaseline]}>
|
||||||
<Link
|
<Link
|
||||||
style={styles.metaItem}
|
style={styles.metaItem}
|
||||||
href={authorHref}
|
href={authorHref}
|
||||||
|
@ -305,10 +305,8 @@ export const PostThreadItem = observer(function PostThreadItem({
|
||||||
lineHeight={1.3}
|
lineHeight={1.3}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
) : (
|
) : undefined}
|
||||||
<View style={{height: 5}} />
|
<PostEmbeds embed={item.post.embed} style={s.mb10} />
|
||||||
)}
|
|
||||||
<PostEmbeds embed={item.post.embed} style={{marginBottom: 10}} />
|
|
||||||
<PostCtrls
|
<PostCtrls
|
||||||
itemHref={itemHref}
|
itemHref={itemHref}
|
||||||
itemTitle={itemTitle}
|
itemTitle={itemTitle}
|
||||||
|
@ -389,6 +387,10 @@ const styles = StyleSheet.create({
|
||||||
paddingTop: 2,
|
paddingTop: 2,
|
||||||
paddingBottom: 2,
|
paddingBottom: 2,
|
||||||
},
|
},
|
||||||
|
metaExpandedLine1: {
|
||||||
|
paddingTop: 5,
|
||||||
|
paddingBottom: 0,
|
||||||
|
},
|
||||||
metaItem: {
|
metaItem: {
|
||||||
paddingRight: 5,
|
paddingRight: 5,
|
||||||
maxWidth: 240,
|
maxWidth: 240,
|
||||||
|
|
|
@ -48,11 +48,7 @@ export const PostVotedBy = observer(function PostVotedBy({
|
||||||
if (view.hasError) {
|
if (view.hasError) {
|
||||||
return (
|
return (
|
||||||
<View>
|
<View>
|
||||||
<ErrorMessage
|
<ErrorMessage message={view.error} onPressTryAgain={onRefresh} />
|
||||||
message={view.error}
|
|
||||||
style={{margin: 6}}
|
|
||||||
onPressTryAgain={onRefresh}
|
|
||||||
/>
|
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -156,7 +156,7 @@ export const Post = observer(function Post({
|
||||||
timestamp={item.post.indexedAt}
|
timestamp={item.post.indexedAt}
|
||||||
/>
|
/>
|
||||||
{replyAuthorDid !== '' && (
|
{replyAuthorDid !== '' && (
|
||||||
<View style={[s.flexRow, s.mb2, {alignItems: 'center'}]}>
|
<View style={[s.flexRow, s.mb2, s.alignCenter]}>
|
||||||
<FontAwesomeIcon
|
<FontAwesomeIcon
|
||||||
icon="reply"
|
icon="reply"
|
||||||
size={9}
|
size={9}
|
||||||
|
@ -187,10 +187,8 @@ export const Post = observer(function Post({
|
||||||
lineHeight={1.3}
|
lineHeight={1.3}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
) : (
|
) : undefined}
|
||||||
<View style={{height: 5}} />
|
<PostEmbeds embed={item.post.embed} style={s.mb10} />
|
||||||
)}
|
|
||||||
<PostEmbeds embed={item.post.embed} style={{marginBottom: 10}} />
|
|
||||||
<PostCtrls
|
<PostCtrls
|
||||||
itemHref={itemHref}
|
itemHref={itemHref}
|
||||||
itemTitle={itemTitle}
|
itemTitle={itemTitle}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React, {useState, useEffect} from 'react'
|
import React, {useState, useEffect} from 'react'
|
||||||
import {observer} from 'mobx-react-lite'
|
import {observer} from 'mobx-react-lite'
|
||||||
import {View} from 'react-native'
|
import {StyleSheet, View} from 'react-native'
|
||||||
import {LoadingPlaceholder} from '../util/LoadingPlaceholder'
|
import {LoadingPlaceholder} from '../util/LoadingPlaceholder'
|
||||||
import {ErrorMessage} from '../util/error/ErrorMessage'
|
import {ErrorMessage} from '../util/error/ErrorMessage'
|
||||||
import {Text} from '../util/text/Text'
|
import {Text} from '../util/text/Text'
|
||||||
|
@ -31,9 +31,9 @@ export const PostText = observer(function PostText({
|
||||||
if (!model || model.isLoading || model.uri !== uri) {
|
if (!model || model.isLoading || model.uri !== uri) {
|
||||||
return (
|
return (
|
||||||
<View>
|
<View>
|
||||||
<LoadingPlaceholder width="100%" height={8} style={{marginTop: 6}} />
|
<LoadingPlaceholder width="100%" height={8} style={styles.mt6} />
|
||||||
<LoadingPlaceholder width="100%" height={8} style={{marginTop: 6}} />
|
<LoadingPlaceholder width="100%" height={8} style={styles.mt6} />
|
||||||
<LoadingPlaceholder width={100} height={8} style={{marginTop: 6}} />
|
<LoadingPlaceholder width={100} height={8} style={styles.mt6} />
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -56,3 +56,7 @@ export const PostText = observer(function PostText({
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
mt6: {marginTop: 6},
|
||||||
|
})
|
||||||
|
|
|
@ -5,6 +5,7 @@ import {
|
||||||
View,
|
View,
|
||||||
FlatList,
|
FlatList,
|
||||||
StyleProp,
|
StyleProp,
|
||||||
|
StyleSheet,
|
||||||
ViewStyle,
|
ViewStyle,
|
||||||
} from 'react-native'
|
} from 'react-native'
|
||||||
import {PostFeedLoadingPlaceholder} from '../util/LoadingPlaceholder'
|
import {PostFeedLoadingPlaceholder} from '../util/LoadingPlaceholder'
|
||||||
|
@ -14,6 +15,7 @@ import {FeedModel} from '../../../state/models/feed-view'
|
||||||
import {FeedItem} from './FeedItem'
|
import {FeedItem} from './FeedItem'
|
||||||
import {PromptButtons} from './PromptButtons'
|
import {PromptButtons} from './PromptButtons'
|
||||||
import {OnScrollCb} from '../../lib/hooks/useOnMainScroll'
|
import {OnScrollCb} from '../../lib/hooks/useOnMainScroll'
|
||||||
|
import {s} from '../../lib/styles'
|
||||||
|
|
||||||
const COMPOSE_PROMPT_ITEM = {_reactKey: '__prompt__'}
|
const COMPOSE_PROMPT_ITEM = {_reactKey: '__prompt__'}
|
||||||
const EMPTY_FEED_ITEM = {_reactKey: '__empty__'}
|
const EMPTY_FEED_ITEM = {_reactKey: '__empty__'}
|
||||||
|
@ -47,7 +49,7 @@ export const Feed = observer(function Feed({
|
||||||
<EmptyState
|
<EmptyState
|
||||||
icon="bars"
|
icon="bars"
|
||||||
message="This feed is empty!"
|
message="This feed is empty!"
|
||||||
style={{paddingVertical: 40}}
|
style={styles.emptyState}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
|
@ -76,7 +78,7 @@ export const Feed = observer(function Feed({
|
||||||
}
|
}
|
||||||
const FeedFooter = () =>
|
const FeedFooter = () =>
|
||||||
feed.isLoading ? (
|
feed.isLoading ? (
|
||||||
<View style={{paddingTop: 20}}>
|
<View style={styles.feedFooter}>
|
||||||
<ActivityIndicator />
|
<ActivityIndicator />
|
||||||
</View>
|
</View>
|
||||||
) : (
|
) : (
|
||||||
|
@ -87,11 +89,7 @@ export const Feed = observer(function Feed({
|
||||||
{!data && <PromptButtons onPressCompose={onPressCompose} />}
|
{!data && <PromptButtons onPressCompose={onPressCompose} />}
|
||||||
{feed.isLoading && !data && <PostFeedLoadingPlaceholder />}
|
{feed.isLoading && !data && <PostFeedLoadingPlaceholder />}
|
||||||
{feed.hasError && (
|
{feed.hasError && (
|
||||||
<ErrorMessage
|
<ErrorMessage message={feed.error} onPressTryAgain={onPressTryAgain} />
|
||||||
message={feed.error}
|
|
||||||
style={{margin: 6}}
|
|
||||||
onPressTryAgain={onPressTryAgain}
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
{feed.hasLoaded && data && (
|
{feed.hasLoaded && data && (
|
||||||
<FlatList
|
<FlatList
|
||||||
|
@ -101,7 +99,7 @@ export const Feed = observer(function Feed({
|
||||||
renderItem={renderItem}
|
renderItem={renderItem}
|
||||||
ListFooterComponent={FeedFooter}
|
ListFooterComponent={FeedFooter}
|
||||||
refreshing={feed.isRefreshing}
|
refreshing={feed.isRefreshing}
|
||||||
contentContainerStyle={{paddingBottom: 100}}
|
contentContainerStyle={s.contentContainer}
|
||||||
onScroll={onScroll}
|
onScroll={onScroll}
|
||||||
onRefresh={onRefresh}
|
onRefresh={onRefresh}
|
||||||
onEndReached={onEndReached}
|
onEndReached={onEndReached}
|
||||||
|
@ -110,3 +108,8 @@ export const Feed = observer(function Feed({
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
feedFooter: {paddingTop: 20},
|
||||||
|
emptyState: {paddingVertical: 40},
|
||||||
|
})
|
||||||
|
|
|
@ -124,7 +124,7 @@ export const FeedItem = observer(function ({
|
||||||
style={[
|
style={[
|
||||||
styles.bottomReplyLine,
|
styles.bottomReplyLine,
|
||||||
{borderColor: pal.colors.replyLine},
|
{borderColor: pal.colors.replyLine},
|
||||||
isNoTop ? {top: 64} : undefined,
|
isNoTop ? styles.bottomReplyLineNoTop : undefined,
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
@ -163,7 +163,7 @@ export const FeedItem = observer(function ({
|
||||||
timestamp={item.post.indexedAt}
|
timestamp={item.post.indexedAt}
|
||||||
/>
|
/>
|
||||||
{!isChild && replyAuthorDid !== '' && (
|
{!isChild && replyAuthorDid !== '' && (
|
||||||
<View style={[s.flexRow, s.mb2, {alignItems: 'center'}]}>
|
<View style={[s.flexRow, s.mb2, s.alignCenter]}>
|
||||||
<FontAwesomeIcon
|
<FontAwesomeIcon
|
||||||
icon="reply"
|
icon="reply"
|
||||||
size={9}
|
size={9}
|
||||||
|
@ -195,9 +195,7 @@ export const FeedItem = observer(function ({
|
||||||
lineHeight={1.3}
|
lineHeight={1.3}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
) : (
|
) : undefined}
|
||||||
<View style={{height: 5}} />
|
|
||||||
)}
|
|
||||||
{item.post.embed ? (
|
{item.post.embed ? (
|
||||||
<PostEmbeds embed={item.post.embed} style={styles.embed} />
|
<PostEmbeds embed={item.post.embed} style={styles.embed} />
|
||||||
) : null}
|
) : null}
|
||||||
|
@ -281,6 +279,7 @@ const styles = StyleSheet.create({
|
||||||
bottom: 0,
|
bottom: 0,
|
||||||
borderLeftWidth: 2,
|
borderLeftWidth: 2,
|
||||||
},
|
},
|
||||||
|
bottomReplyLineNoTop: {top: 64},
|
||||||
includeReason: {
|
includeReason: {
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
paddingLeft: 50,
|
paddingLeft: 50,
|
||||||
|
|
|
@ -54,11 +54,7 @@ export const ProfileFollowers = observer(function ProfileFollowers({
|
||||||
if (view.hasError) {
|
if (view.hasError) {
|
||||||
return (
|
return (
|
||||||
<View>
|
<View>
|
||||||
<ErrorMessage
|
<ErrorMessage message={view.error} onPressTryAgain={onRefresh} />
|
||||||
message={view.error}
|
|
||||||
style={{margin: 6}}
|
|
||||||
onPressTryAgain={onRefresh}
|
|
||||||
/>
|
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,11 +54,7 @@ export const ProfileFollows = observer(function ProfileFollows({
|
||||||
if (view.hasError) {
|
if (view.hasError) {
|
||||||
return (
|
return (
|
||||||
<View>
|
<View>
|
||||||
<ErrorMessage
|
<ErrorMessage message={view.error} onPressTryAgain={onRefresh} />
|
||||||
message={view.error}
|
|
||||||
style={{margin: 6}}
|
|
||||||
onPressTryAgain={onRefresh}
|
|
||||||
/>
|
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -100,22 +100,14 @@ export const ProfileHeader = observer(function ProfileHeader({
|
||||||
<LoadingPlaceholder width="100%" height={120} />
|
<LoadingPlaceholder width="100%" height={120} />
|
||||||
<View
|
<View
|
||||||
style={[pal.view, {borderColor: pal.colors.background}, styles.avi]}>
|
style={[pal.view, {borderColor: pal.colors.background}, styles.avi]}>
|
||||||
<LoadingPlaceholder
|
<LoadingPlaceholder width={80} height={80} style={styles.br40} />
|
||||||
width={80}
|
|
||||||
height={80}
|
|
||||||
style={{borderRadius: 40}}
|
|
||||||
/>
|
|
||||||
</View>
|
</View>
|
||||||
<View style={styles.content}>
|
<View style={styles.content}>
|
||||||
<View style={[styles.buttonsLine]}>
|
<View style={[styles.buttonsLine]}>
|
||||||
<LoadingPlaceholder
|
<LoadingPlaceholder width={100} height={31} style={styles.br50} />
|
||||||
width={100}
|
|
||||||
height={31}
|
|
||||||
style={{borderRadius: 50}}
|
|
||||||
/>
|
|
||||||
</View>
|
</View>
|
||||||
<View style={styles.displayNameLine}>
|
<View style={styles.displayNameLine}>
|
||||||
<Text type="title-xl" style={[pal.text, {lineHeight: 38}]}>
|
<Text type="title-xl" style={[pal.text, styles.title]}>
|
||||||
{view.displayName || view.handle}
|
{view.displayName || view.handle}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
|
@ -208,7 +200,7 @@ export const ProfileHeader = observer(function ProfileHeader({
|
||||||
) : undefined}
|
) : undefined}
|
||||||
</View>
|
</View>
|
||||||
<View style={styles.displayNameLine}>
|
<View style={styles.displayNameLine}>
|
||||||
<Text type="title-xl" style={[pal.text, {lineHeight: 38}]}>
|
<Text type="title-xl" style={[pal.text, styles.title]}>
|
||||||
{view.displayName || view.handle}
|
{view.displayName || view.handle}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
|
@ -349,6 +341,7 @@ const styles = StyleSheet.create({
|
||||||
// paddingLeft: 86,
|
// paddingLeft: 86,
|
||||||
// marginBottom: 14,
|
// marginBottom: 14,
|
||||||
},
|
},
|
||||||
|
title: {lineHeight: 38},
|
||||||
|
|
||||||
handleLine: {
|
handleLine: {
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
|
@ -369,4 +362,7 @@ const styles = StyleSheet.create({
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
marginBottom: 5,
|
marginBottom: 5,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
br40: {borderRadius: 40},
|
||||||
|
br50: {borderRadius: 50},
|
||||||
})
|
})
|
||||||
|
|
|
@ -57,7 +57,7 @@ export const Link = observer(function Link({
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
export const TextLink = observer(function Link({
|
export const TextLink = observer(function TextLink({
|
||||||
type = 'md',
|
type = 'md',
|
||||||
style,
|
style,
|
||||||
href,
|
href,
|
||||||
|
|
|
@ -19,23 +19,15 @@ export function LoadingPlaceholder({
|
||||||
return (
|
return (
|
||||||
<View
|
<View
|
||||||
style={[
|
style={[
|
||||||
|
styles.loadingPlaceholder,
|
||||||
{
|
{
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
backgroundColor: theme.palette.default.backgroundLight,
|
backgroundColor: theme.palette.default.backgroundLight,
|
||||||
borderRadius: 6,
|
|
||||||
overflow: 'hidden',
|
|
||||||
},
|
},
|
||||||
style,
|
style,
|
||||||
]}>
|
]}
|
||||||
<View
|
|
||||||
style={{
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
backgroundColor: theme.palette.default.backgroundLight,
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</View>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,6 +129,9 @@ export function NotificationFeedLoadingPlaceholder() {
|
||||||
}
|
}
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
|
loadingPlaceholder: {
|
||||||
|
borderRadius: 6,
|
||||||
|
},
|
||||||
post: {
|
post: {
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
padding: 10,
|
padding: 10,
|
||||||
|
|
|
@ -128,10 +128,7 @@ export function PostCtrls(opts: PostCtrlsOpts) {
|
||||||
hitSlop={HITSLOP}
|
hitSlop={HITSLOP}
|
||||||
onPress={opts.onPressReply}>
|
onPress={opts.onPressReply}>
|
||||||
<CommentBottomArrow
|
<CommentBottomArrow
|
||||||
style={[
|
style={[defaultCtrlColor, opts.big ? s.mt2 : styles.mt1]}
|
||||||
defaultCtrlColor,
|
|
||||||
opts.big ? {marginTop: 2} : {marginTop: 1},
|
|
||||||
]}
|
|
||||||
strokeWidth={3}
|
strokeWidth={3}
|
||||||
size={opts.big ? 20 : 15}
|
size={opts.big ? 20 : 15}
|
||||||
/>
|
/>
|
||||||
|
@ -181,10 +178,7 @@ export function PostCtrls(opts: PostCtrlsOpts) {
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<HeartIcon
|
<HeartIcon
|
||||||
style={[
|
style={[defaultCtrlColor, opts.big ? styles.mt1 : undefined]}
|
||||||
defaultCtrlColor,
|
|
||||||
opts.big ? {marginTop: 1} : undefined,
|
|
||||||
]}
|
|
||||||
strokeWidth={3}
|
strokeWidth={3}
|
||||||
size={opts.big ? 20 : 16}
|
size={opts.big ? 20 : 16}
|
||||||
/>
|
/>
|
||||||
|
@ -244,4 +238,7 @@ const styles = StyleSheet.create({
|
||||||
ctrlIconUpvoted: {
|
ctrlIconUpvoted: {
|
||||||
color: colors.red3,
|
color: colors.red3,
|
||||||
},
|
},
|
||||||
|
mt1: {
|
||||||
|
marginTop: 1,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
|
@ -67,7 +67,7 @@ export function PostEmbeds({
|
||||||
<AutoSizedImage
|
<AutoSizedImage
|
||||||
uri={embed.images[0].thumb}
|
uri={embed.images[0].thumb}
|
||||||
onPress={() => openLightbox(0)}
|
onPress={() => openLightbox(0)}
|
||||||
containerStyle={{borderRadius: 8}}
|
containerStyle={styles.singleImage}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
|
@ -120,6 +120,9 @@ const styles = StyleSheet.create({
|
||||||
imagesContainer: {
|
imagesContainer: {
|
||||||
marginTop: 4,
|
marginTop: 4,
|
||||||
},
|
},
|
||||||
|
singleImage: {
|
||||||
|
borderRadius: 8,
|
||||||
|
},
|
||||||
extOuter: {
|
extOuter: {
|
||||||
borderWidth: 1,
|
borderWidth: 1,
|
||||||
borderRadius: 8,
|
borderRadius: 8,
|
||||||
|
|
|
@ -41,7 +41,7 @@ export function Selector({
|
||||||
width: middle.width,
|
width: middle.width,
|
||||||
}
|
}
|
||||||
return [left, middle, right]
|
return [left, middle, right]
|
||||||
}, [selectedIndex, items, itemLayouts])
|
}, [selectedIndex, itemLayouts])
|
||||||
|
|
||||||
const underlineStyle = {
|
const underlineStyle = {
|
||||||
backgroundColor: pal.colors.text,
|
backgroundColor: pal.colors.text,
|
||||||
|
|
|
@ -62,8 +62,8 @@ export function UserAvatar({
|
||||||
])
|
])
|
||||||
}, [onSelectNewAvatar])
|
}, [onSelectNewAvatar])
|
||||||
|
|
||||||
const renderSvg = (size: number, initials: string) => (
|
const renderSvg = (svgSize: number, svgInitials: string) => (
|
||||||
<Svg width={size} height={size} viewBox="0 0 100 100">
|
<Svg width={svgSize} height={svgSize} viewBox="0 0 100 100">
|
||||||
<Defs>
|
<Defs>
|
||||||
<LinearGradient id="grad" x1="0" y1="0" x2="1" y2="1">
|
<LinearGradient id="grad" x1="0" y1="0" x2="1" y2="1">
|
||||||
<Stop offset="0" stopColor={gradients.blue.start} stopOpacity="1" />
|
<Stop offset="0" stopColor={gradients.blue.start} stopOpacity="1" />
|
||||||
|
@ -78,7 +78,7 @@ export function UserAvatar({
|
||||||
x="50"
|
x="50"
|
||||||
y="67"
|
y="67"
|
||||||
textAnchor="middle">
|
textAnchor="middle">
|
||||||
{initials}
|
{svgInitials}
|
||||||
</Text>
|
</Text>
|
||||||
</Svg>
|
</Svg>
|
||||||
)
|
)
|
||||||
|
@ -88,7 +88,11 @@ export function UserAvatar({
|
||||||
<TouchableOpacity onPress={handleEditAvatar}>
|
<TouchableOpacity onPress={handleEditAvatar}>
|
||||||
{avatar ? (
|
{avatar ? (
|
||||||
<Image
|
<Image
|
||||||
style={{width: size, height: size, borderRadius: (size / 2) | 0}}
|
style={{
|
||||||
|
width: size,
|
||||||
|
height: size,
|
||||||
|
borderRadius: Math.floor(size / 2),
|
||||||
|
}}
|
||||||
source={{uri: avatar}}
|
source={{uri: avatar}}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
|
@ -104,7 +108,7 @@ export function UserAvatar({
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
) : avatar ? (
|
) : avatar ? (
|
||||||
<Image
|
<Image
|
||||||
style={{width: size, height: size, borderRadius: (size / 2) | 0}}
|
style={{width: size, height: size, borderRadius: Math.floor(size / 2)}}
|
||||||
resizeMode="stretch"
|
resizeMode="stretch"
|
||||||
source={{uri: avatar}}
|
source={{uri: avatar}}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React, {useState, useEffect} from 'react'
|
import React, {useState, useEffect} from 'react'
|
||||||
import {AppBskyActorGetProfile as GetProfile} from '@atproto/api'
|
import {AppBskyActorGetProfile as GetProfile} from '@atproto/api'
|
||||||
import {StyleProp, TextStyle} from 'react-native'
|
import {StyleProp, StyleSheet, TextStyle} from 'react-native'
|
||||||
import {Link} from './Link'
|
import {Link} from './Link'
|
||||||
import {Text} from './text/Text'
|
import {Text} from './text/Text'
|
||||||
import {LoadingPlaceholder} from './LoadingPlaceholder'
|
import {LoadingPlaceholder} from './LoadingPlaceholder'
|
||||||
|
@ -53,7 +53,7 @@ export function UserInfoText({
|
||||||
return () => {
|
return () => {
|
||||||
aborted = true
|
aborted = true
|
||||||
}
|
}
|
||||||
}, [did, store.api.app.bsky])
|
}, [did, store.profiles])
|
||||||
|
|
||||||
let inner
|
let inner
|
||||||
if (didFail) {
|
if (didFail) {
|
||||||
|
@ -73,7 +73,7 @@ export function UserInfoText({
|
||||||
<LoadingPlaceholder
|
<LoadingPlaceholder
|
||||||
width={80}
|
width={80}
|
||||||
height={8}
|
height={8}
|
||||||
style={{position: 'relative', top: 1, left: 2}}
|
style={styles.loadingPlaceholder}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -91,3 +91,7 @@ export function UserInfoText({
|
||||||
|
|
||||||
return inner
|
return inner
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
loadingPlaceholder: {position: 'relative', top: 1, left: 2},
|
||||||
|
})
|
||||||
|
|
|
@ -11,8 +11,8 @@ import {UserAvatar} from './UserAvatar'
|
||||||
import {Text} from './text/Text'
|
import {Text} from './text/Text'
|
||||||
import {MagnifyingGlassIcon} from '../../lib/icons'
|
import {MagnifyingGlassIcon} from '../../lib/icons'
|
||||||
import {useStores} from '../../../state'
|
import {useStores} from '../../../state'
|
||||||
import {useTheme} from '../../lib/ThemeContext'
|
|
||||||
import {usePalette} from '../../lib/hooks/usePalette'
|
import {usePalette} from '../../lib/hooks/usePalette'
|
||||||
|
import {colors} from '../../lib/styles'
|
||||||
|
|
||||||
const HITSLOP = {left: 10, top: 10, right: 10, bottom: 10}
|
const HITSLOP = {left: 10, top: 10, right: 10, bottom: 10}
|
||||||
const BACK_HITSLOP = {left: 10, top: 10, right: 30, bottom: 10}
|
const BACK_HITSLOP = {left: 10, top: 10, right: 30, bottom: 10}
|
||||||
|
@ -26,7 +26,6 @@ export const ViewHeader = observer(function ViewHeader({
|
||||||
subtitle?: string
|
subtitle?: string
|
||||||
canGoBack?: boolean
|
canGoBack?: boolean
|
||||||
}) {
|
}) {
|
||||||
const theme = useTheme()
|
|
||||||
const pal = usePalette('default')
|
const pal = usePalette('default')
|
||||||
const store = useStores()
|
const store = useStores()
|
||||||
const onPressBack = () => {
|
const onPressBack = () => {
|
||||||
|
@ -52,12 +51,12 @@ export const ViewHeader = observer(function ViewHeader({
|
||||||
testID="viewHeaderBackOrMenuBtn"
|
testID="viewHeaderBackOrMenuBtn"
|
||||||
onPress={canGoBack ? onPressBack : onPressMenu}
|
onPress={canGoBack ? onPressBack : onPressMenu}
|
||||||
hitSlop={BACK_HITSLOP}
|
hitSlop={BACK_HITSLOP}
|
||||||
style={canGoBack ? styles.backIcon : styles.backIconWide}>
|
style={canGoBack ? styles.backBtn : styles.backBtnWide}>
|
||||||
{canGoBack ? (
|
{canGoBack ? (
|
||||||
<FontAwesomeIcon
|
<FontAwesomeIcon
|
||||||
size={18}
|
size={18}
|
||||||
icon="angle-left"
|
icon="angle-left"
|
||||||
style={[{marginTop: 6}, pal.text]}
|
style={[styles.backIcon, pal.text]}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<UserAvatar
|
<UserAvatar
|
||||||
|
@ -96,13 +95,10 @@ export const ViewHeader = observer(function ViewHeader({
|
||||||
<FontAwesomeIcon icon="signal" style={pal.text} size={16} />
|
<FontAwesomeIcon icon="signal" style={pal.text} size={16} />
|
||||||
<FontAwesomeIcon
|
<FontAwesomeIcon
|
||||||
icon="x"
|
icon="x"
|
||||||
style={{
|
style={[
|
||||||
backgroundColor: pal.colors.background,
|
styles.littleXIcon,
|
||||||
color: theme.palette.error.background,
|
{backgroundColor: pal.colors.background},
|
||||||
position: 'absolute',
|
]}
|
||||||
right: 7,
|
|
||||||
bottom: 7,
|
|
||||||
}}
|
|
||||||
size={8}
|
size={8}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
|
@ -136,15 +132,18 @@ const styles = StyleSheet.create({
|
||||||
fontWeight: 'normal',
|
fontWeight: 'normal',
|
||||||
},
|
},
|
||||||
|
|
||||||
backIcon: {
|
backBtn: {
|
||||||
width: 30,
|
width: 30,
|
||||||
height: 30,
|
height: 30,
|
||||||
},
|
},
|
||||||
backIconWide: {
|
backBtnWide: {
|
||||||
width: 40,
|
width: 40,
|
||||||
height: 30,
|
height: 30,
|
||||||
marginLeft: 6,
|
marginLeft: 6,
|
||||||
},
|
},
|
||||||
|
backIcon: {
|
||||||
|
marginTop: 6,
|
||||||
|
},
|
||||||
btn: {
|
btn: {
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
|
@ -154,4 +153,10 @@ const styles = StyleSheet.create({
|
||||||
borderRadius: 20,
|
borderRadius: 20,
|
||||||
marginLeft: 4,
|
marginLeft: 4,
|
||||||
},
|
},
|
||||||
|
littleXIcon: {
|
||||||
|
color: colors.red3,
|
||||||
|
position: 'absolute',
|
||||||
|
right: 7,
|
||||||
|
bottom: 7,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
|
@ -5,6 +5,7 @@ import {HorzSwipe} from './gestures/HorzSwipe'
|
||||||
import {useAnimatedValue} from '../../lib/hooks/useAnimatedValue'
|
import {useAnimatedValue} from '../../lib/hooks/useAnimatedValue'
|
||||||
import {OnScrollCb} from '../../lib/hooks/useOnMainScroll'
|
import {OnScrollCb} from '../../lib/hooks/useOnMainScroll'
|
||||||
import {clamp} from '../../../lib/numbers'
|
import {clamp} from '../../../lib/numbers'
|
||||||
|
import {s} from '../../lib/styles'
|
||||||
|
|
||||||
const HEADER_ITEM = {_reactKey: '__header__'}
|
const HEADER_ITEM = {_reactKey: '__header__'}
|
||||||
const SELECTOR_ITEM = {_reactKey: '__selector__'}
|
const SELECTOR_ITEM = {_reactKey: '__selector__'}
|
||||||
|
@ -54,7 +55,7 @@ export function ViewSelector({
|
||||||
setSelectedIndex(clamp(index, 0, sections.length))
|
setSelectedIndex(clamp(index, 0, sections.length))
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
onSelectView?.(selectedIndex)
|
onSelectView?.(selectedIndex)
|
||||||
}, [selectedIndex])
|
}, [selectedIndex, onSelectView])
|
||||||
|
|
||||||
// rendering
|
// rendering
|
||||||
// =
|
// =
|
||||||
|
@ -98,7 +99,7 @@ export function ViewSelector({
|
||||||
onScroll={onScroll}
|
onScroll={onScroll}
|
||||||
onRefresh={onRefresh}
|
onRefresh={onRefresh}
|
||||||
onEndReached={onEndReached}
|
onEndReached={onEndReached}
|
||||||
contentContainerStyle={{paddingBottom: 200}}
|
contentContainerStyle={s.contentContainer}
|
||||||
/>
|
/>
|
||||||
</HorzSwipe>
|
</HorzSwipe>
|
||||||
)
|
)
|
||||||
|
|
|
@ -2,6 +2,7 @@ import React, {useState} from 'react'
|
||||||
import {View} from 'react-native'
|
import {View} from 'react-native'
|
||||||
import {RadioButton} from './RadioButton'
|
import {RadioButton} from './RadioButton'
|
||||||
import {ButtonType} from './Button'
|
import {ButtonType} from './Button'
|
||||||
|
import {s} from '../../../lib/styles'
|
||||||
|
|
||||||
export interface RadioGroupItem {
|
export interface RadioGroupItem {
|
||||||
label: string
|
label: string
|
||||||
|
@ -29,7 +30,7 @@ export function RadioGroup({
|
||||||
{items.map((item, i) => (
|
{items.map((item, i) => (
|
||||||
<RadioButton
|
<RadioButton
|
||||||
key={item.key}
|
key={item.key}
|
||||||
style={i !== 0 ? {marginTop: 2} : undefined}
|
style={i !== 0 ? s.mt2 : undefined}
|
||||||
type={type}
|
type={type}
|
||||||
label={item.label}
|
label={item.label}
|
||||||
isSelected={item.key === selection}
|
isSelected={item.key === selection}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import {
|
||||||
View,
|
View,
|
||||||
} from 'react-native'
|
} from 'react-native'
|
||||||
import {clamp} from 'lodash'
|
import {clamp} from 'lodash'
|
||||||
|
import {s} from '../../../lib/styles'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
panX: Animated.Value
|
panX: Animated.Value
|
||||||
|
@ -111,7 +112,9 @@ export function HorzSwipe({
|
||||||
(Math.abs(gestureState.dx) > swipeDistanceThreshold / 4 ||
|
(Math.abs(gestureState.dx) > swipeDistanceThreshold / 4 ||
|
||||||
Math.abs(gestureState.vx) > swipeVelocityThreshold)
|
Math.abs(gestureState.vx) > swipeVelocityThreshold)
|
||||||
) {
|
) {
|
||||||
const final = ((gestureState.dx / Math.abs(gestureState.dx)) * -1) | 0
|
const final = Math.floor(
|
||||||
|
(gestureState.dx / Math.abs(gestureState.dx)) * -1,
|
||||||
|
)
|
||||||
Animated.timing(panX, {
|
Animated.timing(panX, {
|
||||||
toValue: final,
|
toValue: final,
|
||||||
duration: 100,
|
duration: 100,
|
||||||
|
@ -144,7 +147,7 @@ export function HorzSwipe({
|
||||||
})
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View {...panResponder.panHandlers} style={{flex: 1}}>
|
<View {...panResponder.panHandlers} style={s.h100pct}>
|
||||||
{children}
|
{children}
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
|
|
|
@ -9,6 +9,7 @@ import {
|
||||||
View,
|
View,
|
||||||
} from 'react-native'
|
} from 'react-native'
|
||||||
import {clamp} from 'lodash'
|
import {clamp} from 'lodash'
|
||||||
|
import {s} from '../../../lib/styles'
|
||||||
|
|
||||||
export enum Dir {
|
export enum Dir {
|
||||||
None,
|
None,
|
||||||
|
@ -294,7 +295,7 @@ export function SwipeAndZoom({
|
||||||
})
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View {...panResponder.panHandlers} style={{flex: 1}}>
|
<View {...panResponder.panHandlers} style={s.h100pct}>
|
||||||
{children}
|
{children}
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
|
|
|
@ -47,9 +47,9 @@ export function AutoSizedImage({
|
||||||
setImgInfo({width, height})
|
setImgInfo({width, height})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
(error: any) => {
|
(err: any) => {
|
||||||
if (!aborted) {
|
if (!aborted) {
|
||||||
setError(String(error))
|
setError(String(err))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
|
@ -105,7 +105,7 @@ function ImageLayoutGridInner({
|
||||||
<TouchableWithoutFeedback onPress={() => onPress?.(1)}>
|
<TouchableWithoutFeedback onPress={() => onPress?.(1)}>
|
||||||
<Image source={{uri: uris[1]}} style={size1} />
|
<Image source={{uri: uris[1]}} style={size1} />
|
||||||
</TouchableWithoutFeedback>
|
</TouchableWithoutFeedback>
|
||||||
<View style={{height: 5}} />
|
<View style={styles.hSpace} />
|
||||||
<TouchableWithoutFeedback onPress={() => onPress?.(2)}>
|
<TouchableWithoutFeedback onPress={() => onPress?.(2)}>
|
||||||
<Image source={{uri: uris[2]}} style={size1} />
|
<Image source={{uri: uris[2]}} style={size1} />
|
||||||
</TouchableWithoutFeedback>
|
</TouchableWithoutFeedback>
|
||||||
|
|
|
@ -58,6 +58,8 @@ export const gradients = {
|
||||||
export const s = StyleSheet.create({
|
export const s = StyleSheet.create({
|
||||||
// helpers
|
// helpers
|
||||||
footerSpacer: {height: 100},
|
footerSpacer: {height: 100},
|
||||||
|
contentContainer: {paddingBottom: 200},
|
||||||
|
border1: {borderWidth: 1},
|
||||||
|
|
||||||
// font weights
|
// font weights
|
||||||
fw600: {fontWeight: '600'},
|
fw600: {fontWeight: '600'},
|
||||||
|
@ -140,6 +142,7 @@ export const s = StyleSheet.create({
|
||||||
flexCol: {flexDirection: 'column'},
|
flexCol: {flexDirection: 'column'},
|
||||||
flex1: {flex: 1},
|
flex1: {flex: 1},
|
||||||
alignCenter: {alignItems: 'center'},
|
alignCenter: {alignItems: 'center'},
|
||||||
|
alignBaseline: {alignItems: 'baseline'},
|
||||||
|
|
||||||
// position
|
// position
|
||||||
absolute: {position: 'absolute'},
|
absolute: {position: 'absolute'},
|
||||||
|
|
|
@ -18,7 +18,7 @@ import {Debug} from './screens/Debug'
|
||||||
import {Log} from './screens/Log'
|
import {Log} from './screens/Log'
|
||||||
|
|
||||||
export type ScreenParams = {
|
export type ScreenParams = {
|
||||||
navIdx: [number, number]
|
navIdx: string
|
||||||
params: Record<string, any>
|
params: Record<string, any>
|
||||||
visible: boolean
|
visible: boolean
|
||||||
scrollElRef?: MutableRefObject<FlatList<any> | undefined>
|
scrollElRef?: MutableRefObject<FlatList<any> | undefined>
|
||||||
|
|
|
@ -17,7 +17,7 @@ export const Contacts = ({navIdx, visible}: ScreenParams) => {
|
||||||
if (visible) {
|
if (visible) {
|
||||||
store.nav.setTitle(navIdx, 'Contacts')
|
store.nav.setTitle(navIdx, 'Contacts')
|
||||||
}
|
}
|
||||||
}, [store, visible])
|
}, [store, visible, navIdx])
|
||||||
|
|
||||||
const [searchText, onChangeSearchText] = useState('')
|
const [searchText, onChangeSearchText] = useState('')
|
||||||
const inputRef = useRef<TextInput | null>(null)
|
const inputRef = useRef<TextInput | null>(null)
|
||||||
|
|
|
@ -4,6 +4,7 @@ import {ViewHeader} from '../com/util/ViewHeader'
|
||||||
import {ThemeProvider} from '../lib/ThemeContext'
|
import {ThemeProvider} from '../lib/ThemeContext'
|
||||||
import {PaletteColorName} from '../lib/ThemeContext'
|
import {PaletteColorName} from '../lib/ThemeContext'
|
||||||
import {usePalette} from '../lib/hooks/usePalette'
|
import {usePalette} from '../lib/hooks/usePalette'
|
||||||
|
import {s} from '../lib/styles'
|
||||||
|
|
||||||
import {Text} from '../com/util/text/Text'
|
import {Text} from '../com/util/text/Text'
|
||||||
import {ViewSelector} from '../com/util/ViewSelector'
|
import {ViewSelector} from '../com/util/ViewSelector'
|
||||||
|
@ -48,7 +49,7 @@ function DebugInner({
|
||||||
const renderItem = item => {
|
const renderItem = item => {
|
||||||
return (
|
return (
|
||||||
<View>
|
<View>
|
||||||
<View style={{paddingTop: 10, paddingHorizontal: 10}}>
|
<View style={[s.pt10, s.pl10, s.pr10]}>
|
||||||
<ToggleButton
|
<ToggleButton
|
||||||
type="default-light"
|
type="default-light"
|
||||||
onPress={onToggleColorScheme}
|
onPress={onToggleColorScheme}
|
||||||
|
@ -70,7 +71,7 @@ function DebugInner({
|
||||||
const items = [{currentView}]
|
const items = [{currentView}]
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={[{flex: 1}, pal.view]}>
|
<View style={[s.h100pct, pal.view]}>
|
||||||
<ViewHeader title="Debug panel" />
|
<ViewHeader title="Debug panel" />
|
||||||
<ViewSelector
|
<ViewSelector
|
||||||
swipeEnabled
|
swipeEnabled
|
||||||
|
@ -86,7 +87,7 @@ function DebugInner({
|
||||||
function Heading({label}: {label: string}) {
|
function Heading({label}: {label: string}) {
|
||||||
const pal = usePalette('default')
|
const pal = usePalette('default')
|
||||||
return (
|
return (
|
||||||
<View style={{paddingTop: 10, paddingBottom: 5}}>
|
<View style={[s.pt10, s.pb5]}>
|
||||||
<Text type="title-lg" style={pal.text}>
|
<Text type="title-lg" style={pal.text}>
|
||||||
{label}
|
{label}
|
||||||
</Text>
|
</Text>
|
||||||
|
@ -96,7 +97,7 @@ function Heading({label}: {label: string}) {
|
||||||
|
|
||||||
function BaseView() {
|
function BaseView() {
|
||||||
return (
|
return (
|
||||||
<View style={{paddingHorizontal: 10}}>
|
<View style={[s.pl10, s.pr10]}>
|
||||||
<Heading label="Typography" />
|
<Heading label="Typography" />
|
||||||
<TypographyView />
|
<TypographyView />
|
||||||
<Heading label="Palettes" />
|
<Heading label="Palettes" />
|
||||||
|
@ -109,14 +110,14 @@ function BaseView() {
|
||||||
<EmptyStateView />
|
<EmptyStateView />
|
||||||
<Heading label="Loading placeholders" />
|
<Heading label="Loading placeholders" />
|
||||||
<LoadingPlaceholderView />
|
<LoadingPlaceholderView />
|
||||||
<View style={{height: 200}} />
|
<View style={s.footerSpacer} />
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function ControlsView() {
|
function ControlsView() {
|
||||||
return (
|
return (
|
||||||
<ScrollView style={{paddingHorizontal: 10}}>
|
<ScrollView style={[s.pl10, s.pr10]}>
|
||||||
<Heading label="Buttons" />
|
<Heading label="Buttons" />
|
||||||
<ButtonsView />
|
<ButtonsView />
|
||||||
<Heading label="Dropdown Buttons" />
|
<Heading label="Dropdown Buttons" />
|
||||||
|
@ -125,15 +126,15 @@ function ControlsView() {
|
||||||
<ToggleButtonsView />
|
<ToggleButtonsView />
|
||||||
<Heading label="Radio Buttons" />
|
<Heading label="Radio Buttons" />
|
||||||
<RadioButtonsView />
|
<RadioButtonsView />
|
||||||
<View style={{height: 200}} />
|
<View style={s.footerSpacer} />
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function ErrorView() {
|
function ErrorView() {
|
||||||
return (
|
return (
|
||||||
<View style={{padding: 10}}>
|
<View style={s.p10}>
|
||||||
<View style={{marginBottom: 5}}>
|
<View style={s.mb5}>
|
||||||
<ErrorScreen
|
<ErrorScreen
|
||||||
title="Error screen"
|
title="Error screen"
|
||||||
message="A major error occurred that led the entire screen to fail"
|
message="A major error occurred that led the entire screen to fail"
|
||||||
|
@ -141,22 +142,22 @@ function ErrorView() {
|
||||||
onPressTryAgain={() => {}}
|
onPressTryAgain={() => {}}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
<View style={{marginBottom: 5}}>
|
<View style={s.mb5}>
|
||||||
<ErrorMessage message="This is an error that occurred while things were being done" />
|
<ErrorMessage message="This is an error that occurred while things were being done" />
|
||||||
</View>
|
</View>
|
||||||
<View style={{marginBottom: 5}}>
|
<View style={s.mb5}>
|
||||||
<ErrorMessage
|
<ErrorMessage
|
||||||
message="This is an error that occurred while things were being done"
|
message="This is an error that occurred while things were being done"
|
||||||
numberOfLines={1}
|
numberOfLines={1}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
<View style={{marginBottom: 5}}>
|
<View style={s.mb5}>
|
||||||
<ErrorMessage
|
<ErrorMessage
|
||||||
message="This is an error that occurred while things were being done"
|
message="This is an error that occurred while things were being done"
|
||||||
onPressTryAgain={() => {}}
|
onPressTryAgain={() => {}}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
<View style={{marginBottom: 5}}>
|
<View style={s.mb5}>
|
||||||
<ErrorMessage
|
<ErrorMessage
|
||||||
message="This is an error that occurred while things were being done"
|
message="This is an error that occurred while things were being done"
|
||||||
onPressTryAgain={() => {}}
|
onPressTryAgain={() => {}}
|
||||||
|
@ -171,16 +172,7 @@ function PaletteView({palette}: {palette: PaletteColorName}) {
|
||||||
const defaultPal = usePalette('default')
|
const defaultPal = usePalette('default')
|
||||||
const pal = usePalette(palette)
|
const pal = usePalette(palette)
|
||||||
return (
|
return (
|
||||||
<View
|
<View style={[pal.view, pal.border, s.p10, s.mb5, s.border1]}>
|
||||||
style={[
|
|
||||||
pal.view,
|
|
||||||
pal.border,
|
|
||||||
{
|
|
||||||
borderWidth: 1,
|
|
||||||
padding: 10,
|
|
||||||
marginBottom: 5,
|
|
||||||
},
|
|
||||||
]}>
|
|
||||||
<Text style={[pal.text]}>{palette} colors</Text>
|
<Text style={[pal.text]}>{palette} colors</Text>
|
||||||
<Text style={[pal.textLight]}>Light text</Text>
|
<Text style={[pal.textLight]}>Light text</Text>
|
||||||
<Text style={[pal.link]}>Link text</Text>
|
<Text style={[pal.link]}>Link text</Text>
|
||||||
|
@ -197,21 +189,6 @@ function TypographyView() {
|
||||||
const pal = usePalette('default')
|
const pal = usePalette('default')
|
||||||
return (
|
return (
|
||||||
<View style={[pal.view]}>
|
<View style={[pal.view]}>
|
||||||
<Text type="xxl-thin" style={[pal.text]}>
|
|
||||||
'xxl-thin' lorem ipsum dolor
|
|
||||||
</Text>
|
|
||||||
<Text type="xxl" style={[pal.text]}>
|
|
||||||
'xxl' lorem ipsum dolor
|
|
||||||
</Text>
|
|
||||||
<Text type="xxl-medium" style={[pal.text]}>
|
|
||||||
'xxl-medium' lorem ipsum dolor
|
|
||||||
</Text>
|
|
||||||
<Text type="xxl-bold" style={[pal.text]}>
|
|
||||||
'xxl-bold' lorem ipsum dolor
|
|
||||||
</Text>
|
|
||||||
<Text type="xxl-heavy" style={[pal.text]}>
|
|
||||||
'xxl-heavy' lorem ipsum dolor
|
|
||||||
</Text>
|
|
||||||
<Text type="xl-thin" style={[pal.text]}>
|
<Text type="xl-thin" style={[pal.text]}>
|
||||||
'xl-thin' lorem ipsum dolor
|
'xl-thin' lorem ipsum dolor
|
||||||
</Text>
|
</Text>
|
||||||
|
@ -300,9 +277,6 @@ function TypographyView() {
|
||||||
<Text type="button" style={[pal.text]}>
|
<Text type="button" style={[pal.text]}>
|
||||||
Button
|
Button
|
||||||
</Text>
|
</Text>
|
||||||
<Text type="overline" style={[pal.text]}>
|
|
||||||
Overline
|
|
||||||
</Text>
|
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -325,16 +299,12 @@ function ButtonsView() {
|
||||||
const buttonStyles = {marginRight: 5}
|
const buttonStyles = {marginRight: 5}
|
||||||
return (
|
return (
|
||||||
<View style={[defaultPal.view]}>
|
<View style={[defaultPal.view]}>
|
||||||
<View
|
<View style={[s.flexRow, s.mb5]}>
|
||||||
style={{
|
|
||||||
flexDirection: 'row',
|
|
||||||
marginBottom: 5,
|
|
||||||
}}>
|
|
||||||
<Button type="primary" label="Primary solid" style={buttonStyles} />
|
<Button type="primary" label="Primary solid" style={buttonStyles} />
|
||||||
<Button type="secondary" label="Secondary solid" style={buttonStyles} />
|
<Button type="secondary" label="Secondary solid" style={buttonStyles} />
|
||||||
<Button type="inverted" label="Inverted solid" style={buttonStyles} />
|
<Button type="inverted" label="Inverted solid" style={buttonStyles} />
|
||||||
</View>
|
</View>
|
||||||
<View style={{flexDirection: 'row'}}>
|
<View style={s.flexRow}>
|
||||||
<Button
|
<Button
|
||||||
type="primary-outline"
|
type="primary-outline"
|
||||||
label="Primary outline"
|
label="Primary outline"
|
||||||
|
@ -346,7 +316,7 @@ function ButtonsView() {
|
||||||
style={buttonStyles}
|
style={buttonStyles}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
<View style={{flexDirection: 'row'}}>
|
<View style={s.flexRow}>
|
||||||
<Button
|
<Button
|
||||||
type="primary-light"
|
type="primary-light"
|
||||||
label="Primary light"
|
label="Primary light"
|
||||||
|
@ -358,7 +328,7 @@ function ButtonsView() {
|
||||||
style={buttonStyles}
|
style={buttonStyles}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
<View style={{flexDirection: 'row'}}>
|
<View style={s.flexRow}>
|
||||||
<Button
|
<Button
|
||||||
type="default-light"
|
type="default-light"
|
||||||
label="Default light"
|
label="Default light"
|
||||||
|
@ -390,10 +360,7 @@ function DropdownButtonsView() {
|
||||||
const defaultPal = usePalette('default')
|
const defaultPal = usePalette('default')
|
||||||
return (
|
return (
|
||||||
<View style={[defaultPal.view]}>
|
<View style={[defaultPal.view]}>
|
||||||
<View
|
<View style={s.mb5}>
|
||||||
style={{
|
|
||||||
marginBottom: 5,
|
|
||||||
}}>
|
|
||||||
<DropdownButton
|
<DropdownButton
|
||||||
type="primary"
|
type="primary"
|
||||||
items={DROPDOWN_ITEMS}
|
items={DROPDOWN_ITEMS}
|
||||||
|
@ -401,10 +368,7 @@ function DropdownButtonsView() {
|
||||||
label="Primary button"
|
label="Primary button"
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
<View
|
<View style={s.mb5}>
|
||||||
style={{
|
|
||||||
marginBottom: 5,
|
|
||||||
}}>
|
|
||||||
<DropdownButton type="bare" items={DROPDOWN_ITEMS} menuWidth={200}>
|
<DropdownButton type="bare" items={DROPDOWN_ITEMS} menuWidth={200}>
|
||||||
<Text>Bare</Text>
|
<Text>Bare</Text>
|
||||||
</DropdownButton>
|
</DropdownButton>
|
||||||
|
@ -415,7 +379,7 @@ function DropdownButtonsView() {
|
||||||
|
|
||||||
function ToggleButtonsView() {
|
function ToggleButtonsView() {
|
||||||
const defaultPal = usePalette('default')
|
const defaultPal = usePalette('default')
|
||||||
const buttonStyles = {marginBottom: 5}
|
const buttonStyles = s.mb5
|
||||||
const [isSelected, setIsSelected] = React.useState(false)
|
const [isSelected, setIsSelected] = React.useState(false)
|
||||||
const onToggle = () => setIsSelected(!isSelected)
|
const onToggle = () => setIsSelected(!isSelected)
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -83,14 +83,14 @@ export const Home = observer(function Home({
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={s.flex1}>
|
<View style={s.h100pct}>
|
||||||
<ViewHeader title="Bluesky" subtitle="Private Beta" canGoBack={false} />
|
<ViewHeader title="Bluesky" subtitle="Private Beta" canGoBack={false} />
|
||||||
<Feed
|
<Feed
|
||||||
testID="homeFeed"
|
testID="homeFeed"
|
||||||
key="default"
|
key="default"
|
||||||
feed={store.me.mainFeed}
|
feed={store.me.mainFeed}
|
||||||
scrollElRef={scrollElRef}
|
scrollElRef={scrollElRef}
|
||||||
style={{flex: 1}}
|
style={s.h100pct}
|
||||||
onPressCompose={onPressCompose}
|
onPressCompose={onPressCompose}
|
||||||
onPressTryAgain={onPressTryAgain}
|
onPressTryAgain={onPressTryAgain}
|
||||||
onScroll={onMainScroll}
|
onScroll={onMainScroll}
|
||||||
|
@ -99,9 +99,9 @@ export const Home = observer(function Home({
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
style={[
|
style={[
|
||||||
styles.loadLatest,
|
styles.loadLatest,
|
||||||
store.shell.minimalShellMode
|
!store.shell.minimalShellMode && {
|
||||||
? {bottom: 35}
|
bottom: 60 + clamp(safeAreaInsets.bottom, 15, 30),
|
||||||
: {bottom: 60 + clamp(safeAreaInsets.bottom, 15, 30)},
|
},
|
||||||
]}
|
]}
|
||||||
onPress={onPressLoadLatest}
|
onPress={onPressLoadLatest}
|
||||||
hitSlop={HITSLOP}>
|
hitSlop={HITSLOP}>
|
||||||
|
@ -125,6 +125,7 @@ const styles = StyleSheet.create({
|
||||||
loadLatest: {
|
loadLatest: {
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
left: 20,
|
left: 20,
|
||||||
|
bottom: 35,
|
||||||
shadowColor: '#000',
|
shadowColor: '#000',
|
||||||
shadowOpacity: 0.3,
|
shadowOpacity: 0.3,
|
||||||
shadowOffset: {width: 0, height: 1},
|
shadowOffset: {width: 0, height: 1},
|
||||||
|
|
|
@ -21,7 +21,7 @@ export const Log = observer(function Log({navIdx, visible}: ScreenParams) {
|
||||||
}
|
}
|
||||||
store.shell.setMinimalShellMode(false)
|
store.shell.setMinimalShellMode(false)
|
||||||
store.nav.setTitle(navIdx, 'Log')
|
store.nav.setTitle(navIdx, 'Log')
|
||||||
}, [visible, store])
|
}, [visible, store, navIdx])
|
||||||
|
|
||||||
const toggler = (id: string) => () => {
|
const toggler = (id: string) => () => {
|
||||||
if (expanded.includes(id)) {
|
if (expanded.includes(id)) {
|
||||||
|
@ -52,7 +52,7 @@ export const Log = observer(function Log({navIdx, visible}: ScreenParams) {
|
||||||
<Text type="sm" style={[styles.summary, pal.text]}>
|
<Text type="sm" style={[styles.summary, pal.text]}>
|
||||||
{entry.summary}
|
{entry.summary}
|
||||||
</Text>
|
</Text>
|
||||||
{!!entry.details ? (
|
{entry.details ? (
|
||||||
<FontAwesomeIcon
|
<FontAwesomeIcon
|
||||||
icon={
|
icon={
|
||||||
expanded.includes(entry.id) ? 'angle-up' : 'angle-down'
|
expanded.includes(entry.id) ? 'angle-up' : 'angle-down'
|
||||||
|
|
|
@ -18,9 +18,9 @@ import {s, colors} from '../lib/styles'
|
||||||
import {usePalette} from '../lib/hooks/usePalette'
|
import {usePalette} from '../lib/hooks/usePalette'
|
||||||
|
|
||||||
enum ScreenState {
|
enum ScreenState {
|
||||||
SigninOrCreateAccount,
|
S_SigninOrCreateAccount,
|
||||||
Signin,
|
S_Signin,
|
||||||
CreateAccount,
|
S_CreateAccount,
|
||||||
}
|
}
|
||||||
|
|
||||||
const SigninOrCreateAccount = ({
|
const SigninOrCreateAccount = ({
|
||||||
|
@ -78,14 +78,13 @@ const SigninOrCreateAccount = ({
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Login = observer(
|
export const Login = observer(() => {
|
||||||
(/*{navigation}: RootTabsScreenProps<'Login'>*/) => {
|
|
||||||
const pal = usePalette('default')
|
const pal = usePalette('default')
|
||||||
const [screenState, setScreenState] = useState<ScreenState>(
|
const [screenState, setScreenState] = useState<ScreenState>(
|
||||||
ScreenState.SigninOrCreateAccount,
|
ScreenState.S_SigninOrCreateAccount,
|
||||||
)
|
)
|
||||||
|
|
||||||
if (screenState === ScreenState.SigninOrCreateAccount) {
|
if (screenState === ScreenState.S_SigninOrCreateAccount) {
|
||||||
return (
|
return (
|
||||||
<LinearGradient
|
<LinearGradient
|
||||||
colors={['#007CFF', '#00BCFF']}
|
colors={['#007CFF', '#00BCFF']}
|
||||||
|
@ -95,9 +94,9 @@ export const Login = observer(
|
||||||
<SafeAreaView testID="noSessionView" style={styles.container}>
|
<SafeAreaView testID="noSessionView" style={styles.container}>
|
||||||
<ErrorBoundary>
|
<ErrorBoundary>
|
||||||
<SigninOrCreateAccount
|
<SigninOrCreateAccount
|
||||||
onPressSignin={() => setScreenState(ScreenState.Signin)}
|
onPressSignin={() => setScreenState(ScreenState.S_Signin)}
|
||||||
onPressCreateAccount={() =>
|
onPressCreateAccount={() =>
|
||||||
setScreenState(ScreenState.CreateAccount)
|
setScreenState(ScreenState.S_CreateAccount)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
|
@ -110,17 +109,17 @@ export const Login = observer(
|
||||||
<View style={[styles.container, pal.view]}>
|
<View style={[styles.container, pal.view]}>
|
||||||
<SafeAreaView testID="noSessionView" style={styles.container}>
|
<SafeAreaView testID="noSessionView" style={styles.container}>
|
||||||
<ErrorBoundary>
|
<ErrorBoundary>
|
||||||
{screenState === ScreenState.Signin ? (
|
{screenState === ScreenState.S_Signin ? (
|
||||||
<Signin
|
<Signin
|
||||||
onPressBack={() =>
|
onPressBack={() =>
|
||||||
setScreenState(ScreenState.SigninOrCreateAccount)
|
setScreenState(ScreenState.S_SigninOrCreateAccount)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
) : undefined}
|
) : undefined}
|
||||||
{screenState === ScreenState.CreateAccount ? (
|
{screenState === ScreenState.S_CreateAccount ? (
|
||||||
<CreateAccount
|
<CreateAccount
|
||||||
onPressBack={() =>
|
onPressBack={() =>
|
||||||
setScreenState(ScreenState.SigninOrCreateAccount)
|
setScreenState(ScreenState.S_SigninOrCreateAccount)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
) : undefined}
|
) : undefined}
|
||||||
|
@ -128,8 +127,7 @@ export const Login = observer(
|
||||||
</SafeAreaView>
|
</SafeAreaView>
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
},
|
})
|
||||||
)
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {Button, View} from 'react-native'
|
import {Button, StyleSheet, View} from 'react-native'
|
||||||
import {ViewHeader} from '../com/util/ViewHeader'
|
import {ViewHeader} from '../com/util/ViewHeader'
|
||||||
import {Text} from '../com/util/text/Text'
|
import {Text} from '../com/util/text/Text'
|
||||||
import {useStores} from '../../state'
|
import {useStores} from '../../state'
|
||||||
|
@ -9,13 +9,8 @@ export const NotFound = () => {
|
||||||
return (
|
return (
|
||||||
<View testID="notFoundView">
|
<View testID="notFoundView">
|
||||||
<ViewHeader title="Page not found" />
|
<ViewHeader title="Page not found" />
|
||||||
<View
|
<View style={styles.container}>
|
||||||
style={{
|
<Text style={styles.title}>Page not found</Text>
|
||||||
justifyContent: 'center',
|
|
||||||
alignItems: 'center',
|
|
||||||
paddingTop: 100,
|
|
||||||
}}>
|
|
||||||
<Text style={{fontSize: 40, fontWeight: 'bold'}}>Page not found</Text>
|
|
||||||
<Button
|
<Button
|
||||||
testID="navigateHomeButton"
|
testID="navigateHomeButton"
|
||||||
title="Home"
|
title="Home"
|
||||||
|
@ -25,3 +20,15 @@ export const NotFound = () => {
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
paddingTop: 100,
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
fontSize: 40,
|
||||||
|
fontWeight: 'bold',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
|
@ -5,6 +5,7 @@ import {Feed} from '../com/notifications/Feed'
|
||||||
import {useStores} from '../../state'
|
import {useStores} from '../../state'
|
||||||
import {ScreenParams} from '../routes'
|
import {ScreenParams} from '../routes'
|
||||||
import {useOnMainScroll} from '../lib/hooks/useOnMainScroll'
|
import {useOnMainScroll} from '../lib/hooks/useOnMainScroll'
|
||||||
|
import {s} from '../lib/styles'
|
||||||
|
|
||||||
export const Notifications = ({navIdx, visible}: ScreenParams) => {
|
export const Notifications = ({navIdx, visible}: ScreenParams) => {
|
||||||
const store = useStores()
|
const store = useStores()
|
||||||
|
@ -24,14 +25,14 @@ export const Notifications = ({navIdx, visible}: ScreenParams) => {
|
||||||
store.me.notifications.updateReadState()
|
store.me.notifications.updateReadState()
|
||||||
})
|
})
|
||||||
store.nav.setTitle(navIdx, 'Notifications')
|
store.nav.setTitle(navIdx, 'Notifications')
|
||||||
}, [visible, store])
|
}, [visible, store, navIdx])
|
||||||
|
|
||||||
const onPressTryAgain = () => {
|
const onPressTryAgain = () => {
|
||||||
store.me.notifications.refresh()
|
store.me.notifications.refresh()
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={{flex: 1}}>
|
<View style={s.h100pct}>
|
||||||
<ViewHeader title="Notifications" canGoBack={false} />
|
<ViewHeader title="Notifications" canGoBack={false} />
|
||||||
<Feed
|
<Feed
|
||||||
view={store.me.notifications}
|
view={store.me.notifications}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import React, {useEffect} from 'react'
|
import React, {useEffect} from 'react'
|
||||||
import {View} from 'react-native'
|
import {StyleSheet, View} from 'react-native'
|
||||||
import {observer} from 'mobx-react-lite'
|
import {observer} from 'mobx-react-lite'
|
||||||
import {FeatureExplainer} from '../com/onboard/FeatureExplainer'
|
import {FeatureExplainer} from '../com/onboard/FeatureExplainer'
|
||||||
import {Follows} from '../com/onboard/Follows'
|
import {Follows} from '../com/onboard/Follows'
|
||||||
|
@ -14,7 +14,7 @@ export const Onboard = observer(() => {
|
||||||
if (!OnboardStageOrder.includes(store.onboard.stage)) {
|
if (!OnboardStageOrder.includes(store.onboard.stage)) {
|
||||||
store.onboard.stop()
|
store.onboard.stop()
|
||||||
}
|
}
|
||||||
}, [store.onboard.stage])
|
}, [store.onboard])
|
||||||
|
|
||||||
let Com
|
let Com
|
||||||
if (store.onboard.stage === OnboardStage.Explainers) {
|
if (store.onboard.stage === OnboardStage.Explainers) {
|
||||||
|
@ -26,8 +26,15 @@ export const Onboard = observer(() => {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={{flex: 1, backgroundColor: '#fff'}}>
|
<View style={styles.container}>
|
||||||
<Com />
|
<Com />
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
height: '100%',
|
||||||
|
backgroundColor: '#fff',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
|
@ -16,7 +16,7 @@ export const PostDownvotedBy = ({navIdx, visible, params}: ScreenParams) => {
|
||||||
store.nav.setTitle(navIdx, 'Downvoted by')
|
store.nav.setTitle(navIdx, 'Downvoted by')
|
||||||
store.shell.setMinimalShellMode(false)
|
store.shell.setMinimalShellMode(false)
|
||||||
}
|
}
|
||||||
}, [store, visible])
|
}, [store, visible, navIdx])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View>
|
<View>
|
||||||
|
|
|
@ -16,7 +16,7 @@ export const PostRepostedBy = ({navIdx, visible, params}: ScreenParams) => {
|
||||||
store.nav.setTitle(navIdx, 'Reposted by')
|
store.nav.setTitle(navIdx, 'Reposted by')
|
||||||
store.shell.setMinimalShellMode(false)
|
store.shell.setMinimalShellMode(false)
|
||||||
}
|
}
|
||||||
}, [store, visible])
|
}, [store, visible, navIdx])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View>
|
<View>
|
||||||
|
|
|
@ -6,6 +6,7 @@ import {PostThread as PostThreadComponent} from '../com/post-thread/PostThread'
|
||||||
import {PostThreadViewModel} from '../../state/models/post-thread-view'
|
import {PostThreadViewModel} from '../../state/models/post-thread-view'
|
||||||
import {ScreenParams} from '../routes'
|
import {ScreenParams} from '../routes'
|
||||||
import {useStores} from '../../state'
|
import {useStores} from '../../state'
|
||||||
|
import {s} from '../lib/styles'
|
||||||
|
|
||||||
export const PostThread = ({navIdx, visible, params}: ScreenParams) => {
|
export const PostThread = ({navIdx, visible, params}: ScreenParams) => {
|
||||||
const store = useStores()
|
const store = useStores()
|
||||||
|
@ -14,18 +15,18 @@ export const PostThread = ({navIdx, visible, params}: ScreenParams) => {
|
||||||
const uri = makeRecordUri(name, 'app.bsky.feed.post', rkey)
|
const uri = makeRecordUri(name, 'app.bsky.feed.post', rkey)
|
||||||
const view = useMemo<PostThreadViewModel>(
|
const view = useMemo<PostThreadViewModel>(
|
||||||
() => new PostThreadViewModel(store, {uri}),
|
() => new PostThreadViewModel(store, {uri}),
|
||||||
[uri],
|
[store, uri],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
let aborted = false
|
||||||
|
const threadCleanup = view.registerListeners()
|
||||||
const setTitle = () => {
|
const setTitle = () => {
|
||||||
const author = view.thread?.author
|
const author = view.thread?.post.author
|
||||||
const niceName = author?.handle || name
|
const niceName = author?.handle || name
|
||||||
setViewSubtitle(`by ${niceName}`)
|
setViewSubtitle(`by ${niceName}`)
|
||||||
store.nav.setTitle(navIdx, `Post by ${niceName}`)
|
store.nav.setTitle(navIdx, `Post by ${niceName}`)
|
||||||
}
|
}
|
||||||
useEffect(() => {
|
|
||||||
let aborted = false
|
|
||||||
const threadCleanup = view.registerListeners()
|
|
||||||
if (!visible) {
|
if (!visible) {
|
||||||
return threadCleanup
|
return threadCleanup
|
||||||
}
|
}
|
||||||
|
@ -47,12 +48,12 @@ export const PostThread = ({navIdx, visible, params}: ScreenParams) => {
|
||||||
aborted = true
|
aborted = true
|
||||||
threadCleanup()
|
threadCleanup()
|
||||||
}
|
}
|
||||||
}, [visible, store.nav, store.log, name])
|
}, [visible, store.nav, store.log, store.shell, name, navIdx, view])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={{flex: 1}}>
|
<View style={s.h100pct}>
|
||||||
<ViewHeader title="Post" subtitle={viewSubtitle} />
|
<ViewHeader title="Post" subtitle={viewSubtitle} />
|
||||||
<View style={{flex: 1}}>
|
<View style={s.h100pct}>
|
||||||
<PostThreadComponent uri={uri} view={view} />
|
<PostThreadComponent uri={uri} view={view} />
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
|
@ -15,7 +15,7 @@ export const PostUpvotedBy = ({navIdx, visible, params}: ScreenParams) => {
|
||||||
if (visible) {
|
if (visible) {
|
||||||
store.nav.setTitle(navIdx, 'Liked by')
|
store.nav.setTitle(navIdx, 'Liked by')
|
||||||
}
|
}
|
||||||
}, [store, visible])
|
}, [store, visible, navIdx])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View>
|
<View>
|
||||||
|
|
|
@ -26,9 +26,13 @@ export const Profile = observer(({navIdx, visible, params}: ScreenParams) => {
|
||||||
const [hasSetup, setHasSetup] = useState<boolean>(false)
|
const [hasSetup, setHasSetup] = useState<boolean>(false)
|
||||||
const uiState = React.useMemo(
|
const uiState = React.useMemo(
|
||||||
() => new ProfileUiModel(store, {user: params.name}),
|
() => new ProfileUiModel(store, {user: params.name}),
|
||||||
[params.user],
|
[params.name, store],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
store.nav.setTitle(navIdx, params.name)
|
||||||
|
}, [store, navIdx, params.name])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let aborted = false
|
let aborted = false
|
||||||
const feedCleanup = uiState.feed.registerListeners()
|
const feedCleanup = uiState.feed.registerListeners()
|
||||||
|
@ -38,7 +42,6 @@ export const Profile = observer(({navIdx, visible, params}: ScreenParams) => {
|
||||||
if (hasSetup) {
|
if (hasSetup) {
|
||||||
uiState.update()
|
uiState.update()
|
||||||
} else {
|
} else {
|
||||||
store.nav.setTitle(navIdx, params.name)
|
|
||||||
uiState.setup().then(() => {
|
uiState.setup().then(() => {
|
||||||
if (aborted) {
|
if (aborted) {
|
||||||
return
|
return
|
||||||
|
@ -50,7 +53,7 @@ export const Profile = observer(({navIdx, visible, params}: ScreenParams) => {
|
||||||
aborted = true
|
aborted = true
|
||||||
feedCleanup()
|
feedCleanup()
|
||||||
}
|
}
|
||||||
}, [visible, params.name, store])
|
}, [visible, store, hasSetup, uiState])
|
||||||
|
|
||||||
// events
|
// events
|
||||||
// =
|
// =
|
||||||
|
@ -139,7 +142,7 @@ export const Profile = observer(({navIdx, visible, params}: ScreenParams) => {
|
||||||
<EmptyState
|
<EmptyState
|
||||||
icon={['far', 'message']}
|
icon={['far', 'message']}
|
||||||
message="No posts yet!"
|
message="No posts yet!"
|
||||||
style={{paddingVertical: 40}}
|
style={styles.emptyState}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -187,7 +190,7 @@ export const Profile = observer(({navIdx, visible, params}: ScreenParams) => {
|
||||||
|
|
||||||
function LoadingMoreFooter() {
|
function LoadingMoreFooter() {
|
||||||
return (
|
return (
|
||||||
<View style={{paddingVertical: 20}}>
|
<View style={styles.loadingMoreFooter}>
|
||||||
<ActivityIndicator />
|
<ActivityIndicator />
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
|
@ -202,6 +205,12 @@ const styles = StyleSheet.create({
|
||||||
paddingVertical: 10,
|
paddingVertical: 10,
|
||||||
paddingHorizontal: 14,
|
paddingHorizontal: 14,
|
||||||
},
|
},
|
||||||
|
emptyState: {
|
||||||
|
paddingVertical: 40,
|
||||||
|
},
|
||||||
|
loadingMoreFooter: {
|
||||||
|
paddingVertical: 20,
|
||||||
|
},
|
||||||
endItem: {
|
endItem: {
|
||||||
paddingTop: 20,
|
paddingTop: 20,
|
||||||
paddingBottom: 30,
|
paddingBottom: 30,
|
||||||
|
|
|
@ -14,7 +14,7 @@ export const ProfileFollowers = ({navIdx, visible, params}: ScreenParams) => {
|
||||||
store.nav.setTitle(navIdx, `Followers of ${name}`)
|
store.nav.setTitle(navIdx, `Followers of ${name}`)
|
||||||
store.shell.setMinimalShellMode(false)
|
store.shell.setMinimalShellMode(false)
|
||||||
}
|
}
|
||||||
}, [store, visible, name])
|
}, [store, visible, name, navIdx])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View>
|
<View>
|
||||||
|
|
|
@ -14,7 +14,7 @@ export const ProfileFollows = ({navIdx, visible, params}: ScreenParams) => {
|
||||||
store.nav.setTitle(navIdx, `Followed by ${name}`)
|
store.nav.setTitle(navIdx, `Followed by ${name}`)
|
||||||
store.shell.setMinimalShellMode(false)
|
store.shell.setMinimalShellMode(false)
|
||||||
}
|
}
|
||||||
}, [store, visible, name])
|
}, [store, visible, name, navIdx])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View>
|
<View>
|
||||||
|
|
|
@ -25,7 +25,7 @@ export const Search = ({navIdx, visible, params}: ScreenParams) => {
|
||||||
const [query, setQuery] = useState<string>('')
|
const [query, setQuery] = useState<string>('')
|
||||||
const autocompleteView = useMemo<UserAutocompleteViewModel>(
|
const autocompleteView = useMemo<UserAutocompleteViewModel>(
|
||||||
() => new UserAutocompleteViewModel(store),
|
() => new UserAutocompleteViewModel(store),
|
||||||
[],
|
[store],
|
||||||
)
|
)
|
||||||
const {name} = params
|
const {name} = params
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ export const Search = ({navIdx, visible, params}: ScreenParams) => {
|
||||||
autocompleteView.setup()
|
autocompleteView.setup()
|
||||||
store.nav.setTitle(navIdx, 'Search')
|
store.nav.setTitle(navIdx, 'Search')
|
||||||
}
|
}
|
||||||
}, [store, visible, name])
|
}, [store, visible, name, navIdx, autocompleteView])
|
||||||
|
|
||||||
const onChangeQuery = (text: string) => {
|
const onChangeQuery = (text: string) => {
|
||||||
setQuery(text)
|
setQuery(text)
|
||||||
|
|
|
@ -33,7 +33,7 @@ export const Settings = observer(function Settings({
|
||||||
}
|
}
|
||||||
store.shell.setMinimalShellMode(false)
|
store.shell.setMinimalShellMode(false)
|
||||||
store.nav.setTitle(navIdx, 'Settings')
|
store.nav.setTitle(navIdx, 'Settings')
|
||||||
}, [visible, store])
|
}, [visible, store, navIdx])
|
||||||
|
|
||||||
const onPressSwitchAccount = async (acct: AccountData) => {
|
const onPressSwitchAccount = async (acct: AccountData) => {
|
||||||
setIsSwitching(true)
|
setIsSwitching(true)
|
||||||
|
@ -130,8 +130,8 @@ export const Settings = observer(function Settings({
|
||||||
style={[
|
style={[
|
||||||
pal.view,
|
pal.view,
|
||||||
styles.profile,
|
styles.profile,
|
||||||
|
styles.alignCenter,
|
||||||
s.mb2,
|
s.mb2,
|
||||||
{alignItems: 'center'},
|
|
||||||
isSwitching && styles.dimmed,
|
isSwitching && styles.dimmed,
|
||||||
]}
|
]}
|
||||||
onPress={isSwitching ? undefined : onPressAddAccount}>
|
onPress={isSwitching ? undefined : onPressAddAccount}>
|
||||||
|
@ -142,7 +142,7 @@ export const Settings = observer(function Settings({
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
<View style={{height: 50}} />
|
<View style={styles.spacer} />
|
||||||
<Text type="sm-medium" style={[s.mb5]}>
|
<Text type="sm-medium" style={[s.mb5]}>
|
||||||
Developer tools
|
Developer tools
|
||||||
</Text>
|
</Text>
|
||||||
|
@ -168,6 +168,12 @@ const styles = StyleSheet.create({
|
||||||
dimmed: {
|
dimmed: {
|
||||||
opacity: 0.5,
|
opacity: 0.5,
|
||||||
},
|
},
|
||||||
|
spacer: {
|
||||||
|
height: 50,
|
||||||
|
},
|
||||||
|
alignCenter: {
|
||||||
|
alignItems: 'center',
|
||||||
|
},
|
||||||
title: {
|
title: {
|
||||||
fontSize: 32,
|
fontSize: 32,
|
||||||
fontWeight: 'bold',
|
fontWeight: 'bold',
|
||||||
|
|
|
@ -23,8 +23,7 @@ import {Text} from '../../com/util/text/Text'
|
||||||
import {ToggleButton} from '../../com/util/forms/ToggleButton'
|
import {ToggleButton} from '../../com/util/forms/ToggleButton'
|
||||||
import {usePalette} from '../../lib/hooks/usePalette'
|
import {usePalette} from '../../lib/hooks/usePalette'
|
||||||
|
|
||||||
export const Menu = observer(
|
export const Menu = observer(({onClose}: {onClose: () => void}) => {
|
||||||
({visible, onClose}: {visible: boolean; onClose: () => void}) => {
|
|
||||||
const pal = usePalette('default')
|
const pal = usePalette('default')
|
||||||
const store = useStores()
|
const store = useStores()
|
||||||
|
|
||||||
|
@ -123,18 +122,14 @@ export const Menu = observer(
|
||||||
Search
|
Search
|
||||||
</Text>
|
</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
<View style={[styles.section, pal.border, {paddingTop: 5}]}>
|
<View style={[styles.section, pal.border, s.pt5]}>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
icon={
|
icon={<HomeIcon style={pal.text as StyleProp<ViewStyle>} size="26" />}
|
||||||
<HomeIcon style={pal.text as StyleProp<ViewStyle>} size="26" />
|
|
||||||
}
|
|
||||||
label="Home"
|
label="Home"
|
||||||
url="/"
|
url="/"
|
||||||
/>
|
/>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
icon={
|
icon={<BellIcon style={pal.text as StyleProp<ViewStyle>} size="28" />}
|
||||||
<BellIcon style={pal.text as StyleProp<ViewStyle>} size="28" />
|
|
||||||
}
|
|
||||||
label="Notifications"
|
label="Notifications"
|
||||||
url="/notifications"
|
url="/notifications"
|
||||||
count={store.me.notificationCount}
|
count={store.me.notificationCount}
|
||||||
|
@ -171,15 +166,14 @@ export const Menu = observer(
|
||||||
</View>
|
</View>
|
||||||
<View style={styles.footer}>
|
<View style={styles.footer}>
|
||||||
<Text style={[pal.textLight]}>
|
<Text style={[pal.textLight]}>
|
||||||
Build version {VersionNumber.appVersion} (
|
Build version {VersionNumber.appVersion} ({VersionNumber.buildVersion}
|
||||||
{VersionNumber.buildVersion})
|
)
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
<View style={s.footerSpacer} />
|
<View style={s.footerSpacer} />
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
)
|
)
|
||||||
},
|
})
|
||||||
)
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
view: {
|
view: {
|
||||||
|
|
|
@ -32,7 +32,7 @@ import {Text} from '../../com/util/text/Text'
|
||||||
import {ErrorBoundary} from '../../com/util/ErrorBoundary'
|
import {ErrorBoundary} from '../../com/util/ErrorBoundary'
|
||||||
import {TabsSelector} from './TabsSelector'
|
import {TabsSelector} from './TabsSelector'
|
||||||
import {Composer} from './Composer'
|
import {Composer} from './Composer'
|
||||||
import {colors} from '../../lib/styles'
|
import {s, colors} from '../../lib/styles'
|
||||||
import {clamp} from '../../../lib/numbers'
|
import {clamp} from '../../../lib/numbers'
|
||||||
import {
|
import {
|
||||||
GridIcon,
|
GridIcon,
|
||||||
|
@ -385,7 +385,7 @@ export const MobileShell: React.FC = observer(() => {
|
||||||
/>
|
/>
|
||||||
<Animated.View
|
<Animated.View
|
||||||
style={[
|
style={[
|
||||||
{height: '100%'},
|
s.h100pct,
|
||||||
screenBg,
|
screenBg,
|
||||||
current
|
current
|
||||||
? [
|
? [
|
||||||
|
@ -486,7 +486,7 @@ export const MobileShell: React.FC = observer(() => {
|
||||||
*/
|
*/
|
||||||
type ScreenRenderDesc = MatchResult & {
|
type ScreenRenderDesc = MatchResult & {
|
||||||
key: string
|
key: string
|
||||||
navIdx: [number, number]
|
navIdx: string
|
||||||
current: boolean
|
current: boolean
|
||||||
previous: boolean
|
previous: boolean
|
||||||
isNewTab: boolean
|
isNewTab: boolean
|
||||||
|
@ -514,7 +514,7 @@ function constructScreenRenderDesc(nav: NavigationModel): {
|
||||||
hasNewTab = hasNewTab || tab.isNewTab
|
hasNewTab = hasNewTab || tab.isNewTab
|
||||||
return Object.assign(matchRes, {
|
return Object.assign(matchRes, {
|
||||||
key: `t${tab.id}-s${screen.index}`,
|
key: `t${tab.id}-s${screen.index}`,
|
||||||
navIdx: [tab.id, screen.id],
|
navIdx: `${tab.id}-${screen.id}`,
|
||||||
current: isCurrent,
|
current: isCurrent,
|
||||||
previous: isPrevious,
|
previous: isPrevious,
|
||||||
isNewTab: tab.isNewTab,
|
isNewTab: tab.isNewTab,
|
||||||
|
|
Loading…
Reference in New Issue