Fix a bunch of type errors and add a type-check to the github workflows (#837)
* Add yarn type-check * Rename to yarn typecheck * Fix a collection of type errors * Add typecheck to automated tests * add `dist` to exluded folders tsconfig --------- Co-authored-by: Ansh Nanda <anshnanda10@gmail.com>zio/stable
parent
46c9de7c18
commit
e8843ded5b
|
@ -18,8 +18,10 @@ jobs:
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
- name: Yarn install
|
- name: Yarn install
|
||||||
run: yarn
|
run: yarn
|
||||||
- name: Typescript & Lint check
|
- name: Lint check
|
||||||
run: yarn lint
|
run: yarn lint
|
||||||
|
- name: Type check
|
||||||
|
run: yarn typecheck
|
||||||
testing:
|
testing:
|
||||||
name: Run tests
|
name: Run tests
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
"test-ci": "jest --ci --forceExit --reporters=default --reporters=jest-junit",
|
"test-ci": "jest --ci --forceExit --reporters=default --reporters=jest-junit",
|
||||||
"test-coverage": "jest --coverage",
|
"test-coverage": "jest --coverage",
|
||||||
"lint": "eslint ./src --ext .js,.jsx,.ts,.tsx",
|
"lint": "eslint ./src --ext .js,.jsx,.ts,.tsx",
|
||||||
|
"typecheck": "tsc --project ./tsconfig.check.json",
|
||||||
"e2e:mock-server": "ts-node __e2e__/mock-server.ts",
|
"e2e:mock-server": "ts-node __e2e__/mock-server.ts",
|
||||||
"e2e:metro": "RN_SRC_EXT=e2e.ts,e2e.tsx expo run:ios",
|
"e2e:metro": "RN_SRC_EXT=e2e.ts,e2e.tsx expo run:ios",
|
||||||
"e2e:build": "detox build -c ios.sim.debug",
|
"e2e:build": "detox build -c ios.sim.debug",
|
||||||
|
|
|
@ -11,7 +11,7 @@ export function doPolyfill() {
|
||||||
interface FetchHandlerResponse {
|
interface FetchHandlerResponse {
|
||||||
status: number
|
status: number
|
||||||
headers: Record<string, string>
|
headers: Record<string, string>
|
||||||
body: ArrayBuffer | undefined
|
body: any
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fetchHandler(
|
async function fetchHandler(
|
||||||
|
|
|
@ -74,9 +74,12 @@ export class FeedViewPostsSlice {
|
||||||
}
|
}
|
||||||
|
|
||||||
flattenReplyParent() {
|
flattenReplyParent() {
|
||||||
if (this.items[0].reply?.parent) {
|
if (this.items[0].reply) {
|
||||||
|
const reply = this.items[0].reply
|
||||||
|
if (AppBskyFeedDefs.isPostView(reply.parent)) {
|
||||||
this.isFlattenedReply = true
|
this.isFlattenedReply = true
|
||||||
this.items.splice(0, 0, {post: this.items[0].reply?.parent})
|
this.items.splice(0, 0, {post: reply.parent})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -130,18 +133,19 @@ export class FeedTuner {
|
||||||
|
|
||||||
// turn non-threads with reply parents into threads
|
// turn non-threads with reply parents into threads
|
||||||
for (const slice of slices) {
|
for (const slice of slices) {
|
||||||
|
if (!slice.isThread && !slice.items[0].reason && slice.items[0].reply) {
|
||||||
|
const reply = slice.items[0].reply
|
||||||
if (
|
if (
|
||||||
!slice.isThread &&
|
AppBskyFeedDefs.isPostView(reply.parent) &&
|
||||||
!slice.items[0].reason &&
|
!this.seenUris.has(reply.parent.uri) &&
|
||||||
slice.items[0].reply?.parent &&
|
!soonToBeSeenUris.has(reply.parent.uri)
|
||||||
!this.seenUris.has(slice.items[0].reply?.parent.uri) &&
|
|
||||||
!soonToBeSeenUris.has(slice.items[0].reply?.parent.uri)
|
|
||||||
) {
|
) {
|
||||||
const uri = slice.items[0].reply?.parent.uri
|
const uri = reply.parent.uri
|
||||||
slice.flattenReplyParent()
|
slice.flattenReplyParent()
|
||||||
soonToBeSeenUris.add(uri)
|
soonToBeSeenUris.add(uri)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (const slice of slices) {
|
for (const slice of slices) {
|
||||||
for (const item of slice.items) {
|
for (const item of slice.items) {
|
||||||
|
@ -231,7 +235,12 @@ export class FeedTuner {
|
||||||
}
|
}
|
||||||
|
|
||||||
function getSelfReplyUri(item: FeedViewPost): string | undefined {
|
function getSelfReplyUri(item: FeedViewPost): string | undefined {
|
||||||
return item.reply?.parent.author.did === item.post.author.did
|
if (item.reply) {
|
||||||
? item.reply?.parent.uri
|
if (AppBskyFeedDefs.isPostView(item.reply.parent)) {
|
||||||
|
return item.reply.parent.author.did === item.post.author.did
|
||||||
|
? item.reply.parent.uri
|
||||||
: undefined
|
: undefined
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ import * as React from 'react'
|
||||||
* Helper hook to run persistent timers on views
|
* Helper hook to run persistent timers on views
|
||||||
*/
|
*/
|
||||||
export function useTimer(time: number, handler: () => void) {
|
export function useTimer(time: number, handler: () => void) {
|
||||||
const timer = React.useRef(undefined)
|
const timer = React.useRef<undefined | NodeJS.Timeout>(undefined)
|
||||||
|
|
||||||
// function to restart the timer
|
// function to restart the timer
|
||||||
const reset = React.useCallback(() => {
|
const reset = React.useCallback(() => {
|
||||||
|
|
|
@ -9,6 +9,16 @@ interface Membership {
|
||||||
value: AppBskyGraphListitem.Record
|
value: AppBskyGraphListitem.Record
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface ListitemRecord {
|
||||||
|
uri: string
|
||||||
|
value: AppBskyGraphListitem.Record
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ListitemListResponse {
|
||||||
|
cursor?: string
|
||||||
|
records: ListitemRecord[]
|
||||||
|
}
|
||||||
|
|
||||||
export class ListMembershipModel {
|
export class ListMembershipModel {
|
||||||
// data
|
// data
|
||||||
memberships: Membership[] = []
|
memberships: Membership[] = []
|
||||||
|
@ -32,9 +42,10 @@ export class ListMembershipModel {
|
||||||
// it needs to be replaced with server side list membership queries
|
// it needs to be replaced with server side list membership queries
|
||||||
// -prf
|
// -prf
|
||||||
let cursor
|
let cursor
|
||||||
let records = []
|
let records: ListitemRecord[] = []
|
||||||
for (let i = 0; i < 100; i++) {
|
for (let i = 0; i < 100; i++) {
|
||||||
const res = await this.rootStore.agent.app.bsky.graph.listitem.list({
|
const res: ListitemListResponse =
|
||||||
|
await this.rootStore.agent.app.bsky.graph.listitem.list({
|
||||||
repo: this.rootStore.me.did,
|
repo: this.rootStore.me.did,
|
||||||
cursor,
|
cursor,
|
||||||
limit: PAGE_SIZE,
|
limit: PAGE_SIZE,
|
||||||
|
@ -99,7 +110,7 @@ export class ListMembershipModel {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateTo(uris: string) {
|
async updateTo(uris: string[]) {
|
||||||
for (const uri of uris) {
|
for (const uri of uris) {
|
||||||
await this.add(uri)
|
await this.add(uri)
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import {
|
||||||
AppBskyGraphGetList as GetList,
|
AppBskyGraphGetList as GetList,
|
||||||
AppBskyGraphDefs as GraphDefs,
|
AppBskyGraphDefs as GraphDefs,
|
||||||
AppBskyGraphList,
|
AppBskyGraphList,
|
||||||
|
AppBskyGraphListitem,
|
||||||
} from '@atproto/api'
|
} from '@atproto/api'
|
||||||
import {Image as RNImage} from 'react-native-image-crop-picker'
|
import {Image as RNImage} from 'react-native-image-crop-picker'
|
||||||
import {RootStoreModel} from '../root-store'
|
import {RootStoreModel} from '../root-store'
|
||||||
|
@ -13,6 +14,16 @@ import {bundleAsync} from 'lib/async/bundle'
|
||||||
|
|
||||||
const PAGE_SIZE = 30
|
const PAGE_SIZE = 30
|
||||||
|
|
||||||
|
interface ListitemRecord {
|
||||||
|
uri: string
|
||||||
|
value: AppBskyGraphListitem.Record
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ListitemListResponse {
|
||||||
|
cursor?: string
|
||||||
|
records: ListitemRecord[]
|
||||||
|
}
|
||||||
|
|
||||||
export class ListModel {
|
export class ListModel {
|
||||||
// state
|
// state
|
||||||
isLoading = false
|
isLoading = false
|
||||||
|
@ -33,7 +44,7 @@ export class ListModel {
|
||||||
name,
|
name,
|
||||||
description,
|
description,
|
||||||
avatar,
|
avatar,
|
||||||
}: {name: string; description: string; avatar: RNImage | undefined},
|
}: {name: string; description: string; avatar: RNImage | null | undefined},
|
||||||
) {
|
) {
|
||||||
const record: AppBskyGraphList.Record = {
|
const record: AppBskyGraphList.Record = {
|
||||||
purpose: 'app.bsky.graph.defs#modlist',
|
purpose: 'app.bsky.graph.defs#modlist',
|
||||||
|
@ -124,6 +135,9 @@ export class ListModel {
|
||||||
description: string
|
description: string
|
||||||
avatar: RNImage | null | undefined
|
avatar: RNImage | null | undefined
|
||||||
}) {
|
}) {
|
||||||
|
if (!this.list) {
|
||||||
|
return
|
||||||
|
}
|
||||||
if (!this.isOwner) {
|
if (!this.isOwner) {
|
||||||
throw new Error('Cannot edit this list')
|
throw new Error('Cannot edit this list')
|
||||||
}
|
}
|
||||||
|
@ -157,11 +171,16 @@ export class ListModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
async delete() {
|
async delete() {
|
||||||
|
if (!this.list) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// fetch all the listitem records that belong to this list
|
// fetch all the listitem records that belong to this list
|
||||||
let cursor
|
let cursor
|
||||||
let records = []
|
let records: ListitemRecord[] = []
|
||||||
for (let i = 0; i < 100; i++) {
|
for (let i = 0; i < 100; i++) {
|
||||||
const res = await this.rootStore.agent.app.bsky.graph.listitem.list({
|
const res: ListitemListResponse =
|
||||||
|
await this.rootStore.agent.app.bsky.graph.listitem.list({
|
||||||
repo: this.rootStore.me.did,
|
repo: this.rootStore.me.did,
|
||||||
cursor,
|
cursor,
|
||||||
limit: PAGE_SIZE,
|
limit: PAGE_SIZE,
|
||||||
|
@ -193,6 +212,9 @@ export class ListModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
async subscribe() {
|
async subscribe() {
|
||||||
|
if (!this.list) {
|
||||||
|
return
|
||||||
|
}
|
||||||
await this.rootStore.agent.app.bsky.graph.muteActorList({
|
await this.rootStore.agent.app.bsky.graph.muteActorList({
|
||||||
list: this.list.uri,
|
list: this.list.uri,
|
||||||
})
|
})
|
||||||
|
@ -200,6 +222,9 @@ export class ListModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
async unsubscribe() {
|
async unsubscribe() {
|
||||||
|
if (!this.list) {
|
||||||
|
return
|
||||||
|
}
|
||||||
await this.rootStore.agent.app.bsky.graph.unmuteActorList({
|
await this.rootStore.agent.app.bsky.graph.unmuteActorList({
|
||||||
list: this.list.uri,
|
list: this.list.uri,
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
import {AppBskyActorDefs} from '@atproto/api'
|
import {
|
||||||
|
AppBskyActorDefs,
|
||||||
|
AppBskyGraphGetFollows as GetFollows,
|
||||||
|
} from '@atproto/api'
|
||||||
import {makeAutoObservable, runInAction} from 'mobx'
|
import {makeAutoObservable, runInAction} from 'mobx'
|
||||||
import sampleSize from 'lodash.samplesize'
|
import sampleSize from 'lodash.samplesize'
|
||||||
import {bundleAsync} from 'lib/async/bundle'
|
import {bundleAsync} from 'lib/async/bundle'
|
||||||
|
@ -43,7 +46,8 @@ export class FoafsModel {
|
||||||
{
|
{
|
||||||
let cursor
|
let cursor
|
||||||
for (let i = 0; i < 10; i++) {
|
for (let i = 0; i < 10; i++) {
|
||||||
const res = await this.rootStore.agent.getFollows({
|
const res: GetFollows.Response =
|
||||||
|
await this.rootStore.agent.getFollows({
|
||||||
actor: this.rootStore.me.did,
|
actor: this.rootStore.me.did,
|
||||||
cursor,
|
cursor,
|
||||||
limit: 100,
|
limit: 100,
|
||||||
|
|
|
@ -67,7 +67,7 @@ export class PostsFeedItemModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
get rootUri(): string {
|
get rootUri(): string {
|
||||||
if (this.reply?.root.uri) {
|
if (typeof this.reply?.root.uri === 'string') {
|
||||||
return this.reply.root.uri
|
return this.reply.root.uri
|
||||||
}
|
}
|
||||||
return this.post.uri
|
return this.post.uri
|
||||||
|
|
|
@ -61,7 +61,7 @@ export class ListsListModel {
|
||||||
}
|
}
|
||||||
this._xLoading(replace)
|
this._xLoading(replace)
|
||||||
try {
|
try {
|
||||||
let res
|
let res: GetLists.Response
|
||||||
if (this.source === 'my-modlists') {
|
if (this.source === 'my-modlists') {
|
||||||
res = {
|
res = {
|
||||||
success: true,
|
success: true,
|
||||||
|
@ -170,7 +170,7 @@ async function fetchAllUserLists(
|
||||||
|
|
||||||
let cursor
|
let cursor
|
||||||
for (let i = 0; i < 100; i++) {
|
for (let i = 0; i < 100; i++) {
|
||||||
const res = await store.agent.app.bsky.graph.getLists({
|
const res: GetLists.Response = await store.agent.app.bsky.graph.getLists({
|
||||||
actor: did,
|
actor: did,
|
||||||
cursor,
|
cursor,
|
||||||
limit: 50,
|
limit: 50,
|
||||||
|
@ -199,7 +199,8 @@ async function fetchAllMyMuteLists(
|
||||||
|
|
||||||
let cursor
|
let cursor
|
||||||
for (let i = 0; i < 100; i++) {
|
for (let i = 0; i < 100; i++) {
|
||||||
const res = await store.agent.app.bsky.graph.getListMutes({
|
const res: GetListMutes.Response =
|
||||||
|
await store.agent.app.bsky.graph.getListMutes({
|
||||||
cursor,
|
cursor,
|
||||||
limit: 50,
|
limit: 50,
|
||||||
})
|
})
|
||||||
|
|
|
@ -8,6 +8,12 @@ import {ImageModel} from '../media/image'
|
||||||
import {ListModel} from '../content/list'
|
import {ListModel} from '../content/list'
|
||||||
import {GalleryModel} from '../media/gallery'
|
import {GalleryModel} from '../media/gallery'
|
||||||
|
|
||||||
|
export type ColorMode = 'system' | 'light' | 'dark'
|
||||||
|
|
||||||
|
export function isColorMode(v: unknown): v is ColorMode {
|
||||||
|
return v === 'system' || v === 'light' || v === 'dark'
|
||||||
|
}
|
||||||
|
|
||||||
export interface ConfirmModal {
|
export interface ConfirmModal {
|
||||||
name: 'confirm'
|
name: 'confirm'
|
||||||
title: string
|
title: string
|
||||||
|
@ -189,7 +195,7 @@ export interface ComposerOpts {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ShellUiModel {
|
export class ShellUiModel {
|
||||||
colorMode = 'system'
|
colorMode: ColorMode = 'system'
|
||||||
minimalShellMode = false
|
minimalShellMode = false
|
||||||
isDrawerOpen = false
|
isDrawerOpen = false
|
||||||
isDrawerSwipeDisabled = false
|
isDrawerSwipeDisabled = false
|
||||||
|
@ -216,13 +222,13 @@ export class ShellUiModel {
|
||||||
|
|
||||||
hydrate(v: unknown) {
|
hydrate(v: unknown) {
|
||||||
if (isObj(v)) {
|
if (isObj(v)) {
|
||||||
if (hasProp(v, 'colorMode') && typeof v.colorMode === 'string') {
|
if (hasProp(v, 'colorMode') && isColorMode(v.colorMode)) {
|
||||||
this.colorMode = v.colorMode
|
this.colorMode = v.colorMode
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setColorMode(mode: string) {
|
setColorMode(mode: ColorMode) {
|
||||||
this.colorMode = mode
|
this.colorMode = mode
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import {useState, useEffect} from 'react'
|
import {useState, useEffect} from 'react'
|
||||||
import {useStores} from 'state/index'
|
import {useStores} from 'state/index'
|
||||||
|
import {ImageModel} from 'state/models/media/image'
|
||||||
import * as apilib from 'lib/api/index'
|
import * as apilib from 'lib/api/index'
|
||||||
import {getLinkMeta} from 'lib/link-meta/link-meta'
|
import {getLinkMeta} from 'lib/link-meta/link-meta'
|
||||||
import {getPostAsQuote, getFeedAsEmbed} from 'lib/link-meta/bsky'
|
import {getPostAsQuote, getFeedAsEmbed} from 'lib/link-meta/bsky'
|
||||||
|
@ -90,7 +91,9 @@ export function useExternalLinkFetch({
|
||||||
setExtLink({
|
setExtLink({
|
||||||
...extLink,
|
...extLink,
|
||||||
isLoading: false, // done
|
isLoading: false, // done
|
||||||
localThumb,
|
localThumb: localThumb
|
||||||
|
? new ImageModel(store, localThumb)
|
||||||
|
: undefined,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
return cleanup
|
return cleanup
|
||||||
|
|
|
@ -27,14 +27,14 @@ export const Lightbox = observer(function Lightbox() {
|
||||||
}
|
}
|
||||||
|
|
||||||
let altText = ''
|
let altText = ''
|
||||||
let uri
|
let uri = ''
|
||||||
if (lightbox.name === 'images') {
|
if (lightbox.name === 'images') {
|
||||||
const opts = store.shell.activeLightbox as models.ImagesLightbox
|
const opts = lightbox as models.ImagesLightbox
|
||||||
uri = opts.images[imageIndex].uri
|
uri = opts.images[imageIndex].uri
|
||||||
altText = opts.images[imageIndex].alt
|
altText = opts.images[imageIndex].alt || ''
|
||||||
} else if (store.shell.activeLightbox.name === 'profile-image') {
|
} else if (lightbox.name === 'profile-image') {
|
||||||
const opts = store.shell.activeLightbox as models.ProfileImageLightbox
|
const opts = lightbox as models.ProfileImageLightbox
|
||||||
uri = opts.profileView.avatar
|
uri = opts.profileView.avatar || ''
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -44,11 +44,11 @@ export function Component({
|
||||||
const {track} = useAnalytics()
|
const {track} = useAnalytics()
|
||||||
|
|
||||||
const [isProcessing, setProcessing] = useState<boolean>(false)
|
const [isProcessing, setProcessing] = useState<boolean>(false)
|
||||||
const [name, setName] = useState<string>(list?.list.name || '')
|
const [name, setName] = useState<string>(list?.list?.name || '')
|
||||||
const [description, setDescription] = useState<string>(
|
const [description, setDescription] = useState<string>(
|
||||||
list?.list.description || '',
|
list?.list?.description || '',
|
||||||
)
|
)
|
||||||
const [avatar, setAvatar] = useState<string | undefined>(list?.list.avatar)
|
const [avatar, setAvatar] = useState<string | undefined>(list?.list?.avatar)
|
||||||
const [newAvatar, setNewAvatar] = useState<RNImage | undefined | null>()
|
const [newAvatar, setNewAvatar] = useState<RNImage | undefined | null>()
|
||||||
|
|
||||||
const onPressCancel = useCallback(() => {
|
const onPressCancel = useCallback(() => {
|
||||||
|
@ -59,7 +59,7 @@ export function Component({
|
||||||
async (img: RNImage | null) => {
|
async (img: RNImage | null) => {
|
||||||
if (!img) {
|
if (!img) {
|
||||||
setNewAvatar(null)
|
setNewAvatar(null)
|
||||||
setAvatar(null)
|
setAvatar(undefined)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
track('CreateMuteList:AvatarSelected')
|
track('CreateMuteList:AvatarSelected')
|
||||||
|
|
|
@ -36,7 +36,7 @@ export const Component = observer(
|
||||||
const pal = usePalette('default')
|
const pal = usePalette('default')
|
||||||
const palPrimary = usePalette('primary')
|
const palPrimary = usePalette('primary')
|
||||||
const palInverted = usePalette('inverted')
|
const palInverted = usePalette('inverted')
|
||||||
const [selected, setSelected] = React.useState([])
|
const [selected, setSelected] = React.useState<string[]>([])
|
||||||
|
|
||||||
const listsList: ListsListModel = React.useMemo(
|
const listsList: ListsListModel = React.useMemo(
|
||||||
() => new ListsListModel(store, store.me.did),
|
() => new ListsListModel(store, store.me.did),
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import React from 'react'
|
import * as React from 'react'
|
||||||
import {StyleSheet, View} from 'react-native'
|
import {StyleSheet, View} from 'react-native'
|
||||||
import {observer} from 'mobx-react-lite'
|
import {observer} from 'mobx-react-lite'
|
||||||
import {AppBskyActorDefs} from '@atproto/api'
|
import {AppBskyActorDefs} from '@atproto/api'
|
||||||
|
@ -32,7 +32,9 @@ export const ProfileCard = observer(
|
||||||
noBorder?: boolean
|
noBorder?: boolean
|
||||||
followers?: AppBskyActorDefs.ProfileView[] | undefined
|
followers?: AppBskyActorDefs.ProfileView[] | undefined
|
||||||
overrideModeration?: boolean
|
overrideModeration?: boolean
|
||||||
renderButton?: (profile: AppBskyActorDefs.ProfileViewBasic) => JSX.Element
|
renderButton?: (
|
||||||
|
profile: AppBskyActorDefs.ProfileViewBasic,
|
||||||
|
) => React.ReactNode
|
||||||
}) => {
|
}) => {
|
||||||
const store = useStores()
|
const store = useStores()
|
||||||
const pal = usePalette('default')
|
const pal = usePalette('default')
|
||||||
|
|
|
@ -587,9 +587,9 @@ const styles = StyleSheet.create({
|
||||||
// Word wrapping appears fine on
|
// Word wrapping appears fine on
|
||||||
// mobile but overflows on desktop
|
// mobile but overflows on desktop
|
||||||
handle: isNative
|
handle: isNative
|
||||||
? undefined
|
? {}
|
||||||
: {
|
: {
|
||||||
// eslint-disable-next-line
|
// @ts-ignore web only -prf
|
||||||
wordBreak: 'break-all',
|
wordBreak: 'break-all',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,7 @@ export const BlurView = ({
|
||||||
}: React.PropsWithChildren<BlurViewProps>) => {
|
}: React.PropsWithChildren<BlurViewProps>) => {
|
||||||
// @ts-ignore using an RNW-specific attribute here -prf
|
// @ts-ignore using an RNW-specific attribute here -prf
|
||||||
let blur = `blur(${blurAmount || 10}px`
|
let blur = `blur(${blurAmount || 10}px`
|
||||||
|
// @ts-ignore using an RNW-specific attribute here -prf
|
||||||
style = addStyle(style, {backdropFilter: blur, WebkitBackdropFilter: blur})
|
style = addStyle(style, {backdropFilter: blur, WebkitBackdropFilter: blur})
|
||||||
if (blurType === 'dark') {
|
if (blurType === 'dark') {
|
||||||
style = addStyle(style, styles.dark)
|
style = addStyle(style, styles.dark)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import React from 'react'
|
import * as React from 'react'
|
||||||
import {StyleSheet, View} from 'react-native'
|
import {StyleSheet, View} from 'react-native'
|
||||||
import {usePalette} from 'lib/hooks/usePalette'
|
import {usePalette} from 'lib/hooks/usePalette'
|
||||||
import {Text} from './text/Text'
|
import {Text} from './text/Text'
|
||||||
|
@ -10,6 +10,15 @@ import {isDesktopWeb} from 'platform/detection'
|
||||||
* DSL. See for instance /locale/en/privacy-policy.tsx
|
* DSL. See for instance /locale/en/privacy-policy.tsx
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
interface IsChildProps {
|
||||||
|
isChild?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
// type ReactNodeWithIsChildProp =
|
||||||
|
// | React.ReactElement<IsChildProps>
|
||||||
|
// | React.ReactElement<IsChildProps>[]
|
||||||
|
// | React.ReactNode
|
||||||
|
|
||||||
export function H1({children}: React.PropsWithChildren<{}>) {
|
export function H1({children}: React.PropsWithChildren<{}>) {
|
||||||
const pal = usePalette('default')
|
const pal = usePalette('default')
|
||||||
return (
|
return (
|
||||||
|
@ -55,10 +64,7 @@ export function P({children}: React.PropsWithChildren<{}>) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function UL({
|
export function UL({children, isChild}: React.PropsWithChildren<IsChildProps>) {
|
||||||
children,
|
|
||||||
isChild,
|
|
||||||
}: React.PropsWithChildren<{isChild: boolean}>) {
|
|
||||||
return (
|
return (
|
||||||
<View style={[styles.ul, isChild && styles.ulChild]}>
|
<View style={[styles.ul, isChild && styles.ulChild]}>
|
||||||
{markChildProps(children)}
|
{markChildProps(children)}
|
||||||
|
@ -66,10 +72,7 @@ export function UL({
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function OL({
|
export function OL({children, isChild}: React.PropsWithChildren<IsChildProps>) {
|
||||||
children,
|
|
||||||
isChild,
|
|
||||||
}: React.PropsWithChildren<{isChild: boolean}>) {
|
|
||||||
return (
|
return (
|
||||||
<View style={[styles.ol, isChild && styles.olChild]}>
|
<View style={[styles.ol, isChild && styles.olChild]}>
|
||||||
{markChildProps(children)}
|
{markChildProps(children)}
|
||||||
|
@ -122,10 +125,13 @@ export function EM({children}: React.PropsWithChildren<{}>) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function markChildProps(children) {
|
function markChildProps(children: React.ReactNode) {
|
||||||
return React.Children.map(children, child => {
|
return React.Children.map(children, child => {
|
||||||
if (React.isValidElement(child)) {
|
if (React.isValidElement(child)) {
|
||||||
return React.cloneElement(child, {isChild: true})
|
return React.cloneElement<IsChildProps>(
|
||||||
|
child as React.ReactElement<IsChildProps>,
|
||||||
|
{isChild: true},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
return child
|
return child
|
||||||
})
|
})
|
||||||
|
|
|
@ -70,7 +70,9 @@ export function UserInfoText({
|
||||||
numberOfLines={1}
|
numberOfLines={1}
|
||||||
href={`/profile/${profile.handle}`}
|
href={`/profile/${profile.handle}`}
|
||||||
text={`${prefix || ''}${sanitizeDisplayName(
|
text={`${prefix || ''}${sanitizeDisplayName(
|
||||||
profile[attr] || profile.handle,
|
typeof profile[attr] === 'string' && profile[attr]
|
||||||
|
? (profile[attr] as string)
|
||||||
|
: profile.handle,
|
||||||
)}`}
|
)}`}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|
|
@ -38,6 +38,7 @@ import {NavigationProp} from 'lib/routes/types'
|
||||||
import {isDesktopWeb} from 'platform/detection'
|
import {isDesktopWeb} from 'platform/detection'
|
||||||
import {pluralize} from 'lib/strings/helpers'
|
import {pluralize} from 'lib/strings/helpers'
|
||||||
import {formatCount} from 'view/com/util/numeric/format'
|
import {formatCount} from 'view/com/util/numeric/format'
|
||||||
|
import {isColorMode} from 'state/models/ui/shell'
|
||||||
|
|
||||||
type Props = NativeStackScreenProps<CommonNavigatorParams, 'Settings'>
|
type Props = NativeStackScreenProps<CommonNavigatorParams, 'Settings'>
|
||||||
export const SettingsScreen = withAuthRequired(
|
export const SettingsScreen = withAuthRequired(
|
||||||
|
@ -299,20 +300,26 @@ export const SettingsScreen = withAuthRequired(
|
||||||
value="system"
|
value="system"
|
||||||
label="System"
|
label="System"
|
||||||
left
|
left
|
||||||
onChange={(v: string) => store.shell.setColorMode(v)}
|
onChange={(v: string) =>
|
||||||
|
store.shell.setColorMode(isColorMode(v) ? v : 'system')
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
<SelectableBtn
|
<SelectableBtn
|
||||||
current={store.shell.colorMode}
|
current={store.shell.colorMode}
|
||||||
value="light"
|
value="light"
|
||||||
label="Light"
|
label="Light"
|
||||||
onChange={(v: string) => store.shell.setColorMode(v)}
|
onChange={(v: string) =>
|
||||||
|
store.shell.setColorMode(isColorMode(v) ? v : 'system')
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
<SelectableBtn
|
<SelectableBtn
|
||||||
current={store.shell.colorMode}
|
current={store.shell.colorMode}
|
||||||
value="dark"
|
value="dark"
|
||||||
label="Dark"
|
label="Dark"
|
||||||
right
|
right
|
||||||
onChange={(v: string) => store.shell.setColorMode(v)}
|
onChange={(v: string) =>
|
||||||
|
store.shell.setColorMode(isColorMode(v) ? v : 'system')
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
|
@ -34,7 +34,7 @@ import {
|
||||||
SatelliteDishIconSolid,
|
SatelliteDishIconSolid,
|
||||||
} from 'lib/icons'
|
} from 'lib/icons'
|
||||||
import {getCurrentRoute, isTab, isStateAtTabRoot} from 'lib/routes/helpers'
|
import {getCurrentRoute, isTab, isStateAtTabRoot} from 'lib/routes/helpers'
|
||||||
import {NavigationProp} from 'lib/routes/types'
|
import {NavigationProp, CommonNavigatorParams} from 'lib/routes/types'
|
||||||
import {router} from '../../../routes'
|
import {router} from '../../../routes'
|
||||||
|
|
||||||
const ProfileCard = observer(() => {
|
const ProfileCard = observer(() => {
|
||||||
|
@ -100,7 +100,8 @@ const NavItem = observer(
|
||||||
let isCurrent =
|
let isCurrent =
|
||||||
currentRouteInfo.name === 'Profile'
|
currentRouteInfo.name === 'Profile'
|
||||||
? isTab(currentRouteInfo.name, pathName) &&
|
? isTab(currentRouteInfo.name, pathName) &&
|
||||||
currentRouteInfo.params.name === store.me.handle
|
(currentRouteInfo.params as CommonNavigatorParams['Profile']).name ===
|
||||||
|
store.me.handle
|
||||||
: isTab(currentRouteInfo.name, pathName)
|
: isTab(currentRouteInfo.name, pathName)
|
||||||
const {onPress} = useLinkProps({to: href})
|
const {onPress} = useLinkProps({to: href})
|
||||||
const onPressWrapped = React.useCallback(
|
const onPressWrapped = React.useCallback(
|
||||||
|
@ -122,6 +123,7 @@ const NavItem = observer(
|
||||||
<PressableWithHover
|
<PressableWithHover
|
||||||
style={styles.navItemWrapper}
|
style={styles.navItemWrapper}
|
||||||
hoverStyle={pal.viewLight}
|
hoverStyle={pal.viewLight}
|
||||||
|
// @ts-ignore the function signature differs on web -prf
|
||||||
onPress={onPressWrapped}
|
onPress={onPressWrapped}
|
||||||
// @ts-ignore web only -prf
|
// @ts-ignore web only -prf
|
||||||
href={href}
|
href={href}
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"extends": "./tsconfig.json",
|
||||||
|
"exclude": ["__e2e__", "dist"],
|
||||||
|
}
|
Loading…
Reference in New Issue