Merge branch 'main' into custom-algos
This commit is contained in:
commit
7aa1d9010e
99 changed files with 4234 additions and 716 deletions
|
@ -33,42 +33,50 @@ export function TEAM_HANDLES(serviceUrl: string) {
|
|||
}
|
||||
}
|
||||
|
||||
// NOTE
|
||||
// this is a temporary list that we periodically update
|
||||
// it is used in the search interface if the user doesn't follow anybody
|
||||
// -prf
|
||||
export const PROD_SUGGESTED_FOLLOWS = [
|
||||
'faithlove.art',
|
||||
'danielkoeth.bsky.social',
|
||||
'bsky.app',
|
||||
'jay.bsky.team',
|
||||
'pfrazee.com',
|
||||
'why.bsky.team',
|
||||
'support.bsky.team',
|
||||
'dholms.xyz',
|
||||
'emily.bsky.team',
|
||||
'rose.bsky.team',
|
||||
'jack.bsky.social',
|
||||
'earthquake.bsky.social',
|
||||
'faithlove.art',
|
||||
'annaghughes.bsky.social',
|
||||
'astrokatie.com',
|
||||
'whysharksmatter.bsky.social',
|
||||
'jamesgunn.bsky.social',
|
||||
'seangunn.bsky.social',
|
||||
'kumail.bsky.social',
|
||||
'craignewmark.bsky.social',
|
||||
'grimes.bsky.social',
|
||||
'xychelsea.tv',
|
||||
'catsofyore.bsky.social',
|
||||
'mcq.bsky.social',
|
||||
'mmasnick.bsky.social',
|
||||
'nitasha.bsky.social',
|
||||
'kenklippenstein.bsky.social',
|
||||
'jaypeters.bsky.social',
|
||||
'miyagawa.bsky.social',
|
||||
'anildash.com',
|
||||
'tiffani.bsky.social',
|
||||
'kelseyhightower.com',
|
||||
'aliafonzy.bsky.social',
|
||||
'tszzl.bsky.social',
|
||||
'bradfitz.com',
|
||||
'danabramov.bsky.social',
|
||||
'shinyakato.dev',
|
||||
'karpathy.bsky.social',
|
||||
'lookitup.baby',
|
||||
'pariss.blacktechpipeline.com',
|
||||
'swiftonsecurity.com',
|
||||
'ericajoy.astrel.la',
|
||||
'b0rk.jvns.ca',
|
||||
'vickiboykis.com',
|
||||
'brooke.vibe.camp',
|
||||
'mollywhite.net',
|
||||
'amir.blue',
|
||||
'zoink.bsky.social',
|
||||
'moskov.bsky.social',
|
||||
'neilhimself.bsky.social',
|
||||
'kylierobison.com',
|
||||
'carnage4life.bsky.social',
|
||||
'lolennui.bsky.social',
|
||||
]
|
||||
export const STAGING_SUGGESTED_FOLLOWS = ['arcalinea', 'paul', 'paul2'].map(
|
||||
handle => `${handle}.staging.bsky.dev`,
|
||||
|
|
20
src/lib/hooks/useSetTitle.ts
Normal file
20
src/lib/hooks/useSetTitle.ts
Normal file
|
@ -0,0 +1,20 @@
|
|||
import {useEffect} from 'react'
|
||||
import {useNavigation} from '@react-navigation/native'
|
||||
|
||||
import {NavigationProp} from 'lib/routes/types'
|
||||
import {bskyTitle} from 'lib/strings/headings'
|
||||
import {useStores} from 'state/index'
|
||||
|
||||
/**
|
||||
* Requires consuming component to be wrapped in `observer`:
|
||||
* https://stackoverflow.com/a/71488009
|
||||
*/
|
||||
export function useSetTitle(title?: string) {
|
||||
const navigation = useNavigation<NavigationProp>()
|
||||
const {unreadCountLabel} = useStores().me.notifications
|
||||
useEffect(() => {
|
||||
if (title) {
|
||||
navigation.setOptions({title: bskyTitle(title, unreadCountLabel)})
|
||||
}
|
||||
}, [title, navigation, unreadCountLabel])
|
||||
}
|
|
@ -320,6 +320,35 @@ export function MoonIcon({
|
|||
)
|
||||
}
|
||||
|
||||
// Copyright (c) 2020 Refactoring UI Inc.
|
||||
// https://github.com/tailwindlabs/heroicons/blob/master/LICENSE
|
||||
export function SunIcon({
|
||||
style,
|
||||
size,
|
||||
strokeWidth = 1.5,
|
||||
}: {
|
||||
style?: StyleProp<ViewStyle>
|
||||
size?: string | number
|
||||
strokeWidth?: number
|
||||
}) {
|
||||
return (
|
||||
<Svg
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
width={size || 32}
|
||||
height={size || 32}
|
||||
strokeWidth={strokeWidth}
|
||||
stroke="currentColor"
|
||||
style={style}>
|
||||
<Path
|
||||
d="M12 3V5.25M18.364 5.63604L16.773 7.22703M21 12H18.75M18.364 18.364L16.773 16.773M12 18.75V21M7.22703 16.773L5.63604 18.364M5.25 12H3M7.22703 7.22703L5.63604 5.63604M15.75 12C15.75 14.0711 14.0711 15.75 12 15.75C9.92893 15.75 8.25 14.0711 8.25 12C8.25 9.92893 9.92893 8.25 12 8.25C14.0711 8.25 15.75 9.92893 15.75 12Z"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</Svg>
|
||||
)
|
||||
}
|
||||
|
||||
// Copyright (c) 2020 Refactoring UI Inc.
|
||||
// https://github.com/tailwindlabs/heroicons/blob/master/LICENSE
|
||||
export function UserIcon({
|
||||
|
@ -828,3 +857,29 @@ export function InfoCircleIcon({
|
|||
</Svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function HandIcon({
|
||||
style,
|
||||
size,
|
||||
strokeWidth = 1.5,
|
||||
}: {
|
||||
style?: StyleProp<TextStyle>
|
||||
size?: string | number
|
||||
strokeWidth?: number
|
||||
}) {
|
||||
return (
|
||||
<Svg
|
||||
width={size}
|
||||
height={size}
|
||||
viewBox="0 0 76 76"
|
||||
stroke="currentColor"
|
||||
strokeWidth={strokeWidth}
|
||||
strokeLinecap="round"
|
||||
fill="none"
|
||||
style={style}>
|
||||
<Path d="M33.5 37V11.5C33.5 8.46243 31.0376 6 28 6V6C24.9624 6 22.5 8.46243 22.5 11.5V48V48C22.5 48.5802 21.8139 48.8874 21.3811 48.501L13.2252 41.2189C10.72 38.9821 6.81945 39.4562 4.92296 42.228L4.77978 42.4372C3.17708 44.7796 3.50863 47.9385 5.56275 49.897L16.0965 59.9409C20.9825 64.5996 26.7533 68.231 33.0675 70.6201V70.6201C38.8234 72.798 45.1766 72.798 50.9325 70.6201L51.9256 70.2444C57.4044 68.1713 61.8038 63.9579 64.1113 58.5735V58.5735C65.6874 54.8962 66.5 50.937 66.5 46.9362V22.5C66.5 19.4624 64.0376 17 61 17V17C57.9624 17 55.5 19.4624 55.5 22.5V36.5" />
|
||||
<Path d="M55.5 37V11.5C55.5 8.46243 53.0376 6 50 6V6C46.9624 6 44.5 8.46243 44.5 11.5V37" />
|
||||
<Path d="M44.5 37V8.5C44.5 5.46243 42.0376 3 39 3V3C35.9624 3 33.5 5.46243 33.5 8.5V37" />
|
||||
</Svg>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import {
|
||||
AppBskyActorDefs,
|
||||
AppBskyGraphDefs,
|
||||
AppBskyEmbedRecordWithMedia,
|
||||
AppBskyEmbedRecord,
|
||||
AppBskyEmbedImages,
|
||||
|
@ -16,6 +17,7 @@ import {
|
|||
Label,
|
||||
LabelValGroup,
|
||||
ModerationBehaviorCode,
|
||||
ModerationBehavior,
|
||||
PostModeration,
|
||||
ProfileModeration,
|
||||
PostLabelInfo,
|
||||
|
@ -127,11 +129,15 @@ export function getPostModeration(
|
|||
|
||||
// muting
|
||||
if (postInfo.isMuted) {
|
||||
let msg = 'Post from an account you muted.'
|
||||
if (postInfo.mutedByList) {
|
||||
msg = `Muted by ${postInfo.mutedByList.name}`
|
||||
}
|
||||
return {
|
||||
avatar,
|
||||
list: hide('Post from an account you muted.'),
|
||||
thread: warn('Post from an account you muted.'),
|
||||
view: warn('Post from an account you muted.'),
|
||||
list: isMute(hide(msg)),
|
||||
thread: isMute(warn(msg)),
|
||||
view: isMute(warn(msg)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -273,6 +279,7 @@ export function getProfileViewBasicLabelInfo(
|
|||
profileLabels: filterProfileLabels(profile.labels),
|
||||
isMuted: profile.viewer?.muted || false,
|
||||
isBlocking: !!profile.viewer?.blocking || false,
|
||||
isBlockedBy: !!profile.viewer?.blockedBy || false,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -302,6 +309,21 @@ export function getEmbedMuted(embed?: Embed): boolean {
|
|||
return false
|
||||
}
|
||||
|
||||
export function getEmbedMutedByList(
|
||||
embed?: Embed,
|
||||
): AppBskyGraphDefs.ListViewBasic | undefined {
|
||||
if (!embed) {
|
||||
return undefined
|
||||
}
|
||||
if (
|
||||
AppBskyEmbedRecord.isView(embed) &&
|
||||
AppBskyEmbedRecord.isViewRecord(embed.record)
|
||||
) {
|
||||
return embed.record.author.viewer?.mutedByList
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
export function getEmbedBlocking(embed?: Embed): boolean {
|
||||
if (!embed) {
|
||||
return false
|
||||
|
@ -401,6 +423,11 @@ function warnContent(reason: string) {
|
|||
}
|
||||
}
|
||||
|
||||
function isMute(behavior: ModerationBehavior): ModerationBehavior {
|
||||
behavior.isMute = true
|
||||
return behavior
|
||||
}
|
||||
|
||||
function warnImages(reason: string) {
|
||||
return {
|
||||
behavior: ModerationBehaviorCode.WarnImages,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {ComAtprotoLabelDefs} from '@atproto/api'
|
||||
import {ComAtprotoLabelDefs, AppBskyGraphDefs} from '@atproto/api'
|
||||
import {LabelPreferencesModel} from 'state/models/ui/preferences'
|
||||
|
||||
export type Label = ComAtprotoLabelDefs.Label
|
||||
|
@ -22,6 +22,7 @@ export interface PostLabelInfo {
|
|||
accountLabels: Label[]
|
||||
profileLabels: Label[]
|
||||
isMuted: boolean
|
||||
mutedByList?: AppBskyGraphDefs.ListViewBasic
|
||||
isBlocking: boolean
|
||||
isBlockedBy: boolean
|
||||
}
|
||||
|
@ -44,6 +45,7 @@ export enum ModerationBehaviorCode {
|
|||
|
||||
export interface ModerationBehavior {
|
||||
behavior: ModerationBehaviorCode
|
||||
isMute?: boolean
|
||||
noOverride?: boolean
|
||||
reason?: string
|
||||
}
|
||||
|
|
|
@ -1,12 +1,16 @@
|
|||
import {
|
||||
openPicker as openPickerFn,
|
||||
openCamera as openCameraFn,
|
||||
openCropper as openCropperFn,
|
||||
ImageOrVideo,
|
||||
Image as RNImage,
|
||||
} from 'react-native-image-crop-picker'
|
||||
import {RootStoreModel} from 'state/index'
|
||||
import {PickerOpts, CameraOpts, CropperOptions} from './types'
|
||||
import {Image as RNImage} from 'react-native-image-crop-picker'
|
||||
import {CameraOpts, CropperOptions} from './types'
|
||||
import {
|
||||
ImagePickerOptions,
|
||||
launchImageLibraryAsync,
|
||||
MediaTypeOptions,
|
||||
} from 'expo-image-picker'
|
||||
import {getDataUriSize} from './util'
|
||||
|
||||
/**
|
||||
* NOTE
|
||||
|
@ -19,27 +23,22 @@ import {Image as RNImage} from 'react-native-image-crop-picker'
|
|||
|
||||
export async function openPicker(
|
||||
_store: RootStoreModel,
|
||||
opts?: PickerOpts,
|
||||
): Promise<RNImage[]> {
|
||||
const items = await openPickerFn({
|
||||
mediaType: 'photo', // TODO: eventually add other media types
|
||||
multiple: opts?.multiple,
|
||||
maxFiles: opts?.maxFiles,
|
||||
forceJpg: true, // ios only
|
||||
compressImageQuality: 0.8,
|
||||
opts?: ImagePickerOptions,
|
||||
) {
|
||||
const response = await launchImageLibraryAsync({
|
||||
exif: false,
|
||||
mediaTypes: MediaTypeOptions.Images,
|
||||
quality: 1,
|
||||
...opts,
|
||||
})
|
||||
|
||||
const toMedia = (item: ImageOrVideo) => ({
|
||||
path: item.path,
|
||||
mime: item.mime,
|
||||
size: item.size,
|
||||
width: item.width,
|
||||
height: item.height,
|
||||
})
|
||||
if (Array.isArray(items)) {
|
||||
return items.map(toMedia)
|
||||
}
|
||||
return [toMedia(items)]
|
||||
return (response.assets ?? []).map(image => ({
|
||||
mime: 'image/jpeg',
|
||||
height: image.height,
|
||||
width: image.width,
|
||||
path: image.uri,
|
||||
size: getDataUriSize(image.uri),
|
||||
}))
|
||||
}
|
||||
|
||||
export async function openCamera(
|
||||
|
@ -55,6 +54,7 @@ export async function openCamera(
|
|||
forceJpg: true, // ios only
|
||||
compressImageQuality: 0.8,
|
||||
})
|
||||
|
||||
return {
|
||||
path: item.path,
|
||||
mime: item.mime,
|
||||
|
@ -67,11 +67,10 @@ export async function openCamera(
|
|||
export async function openCropper(
|
||||
_store: RootStoreModel,
|
||||
opts: CropperOptions,
|
||||
): Promise<RNImage> {
|
||||
) {
|
||||
const item = await openCropperFn({
|
||||
...opts,
|
||||
forceJpg: true, // ios only
|
||||
compressImageQuality: 0.8,
|
||||
})
|
||||
|
||||
return {
|
||||
|
|
|
@ -5,13 +5,19 @@ export type {NativeStackScreenProps} from '@react-navigation/native-stack'
|
|||
|
||||
export type CommonNavigatorParams = {
|
||||
NotFound: undefined
|
||||
Moderation: undefined
|
||||
ModerationMuteLists: undefined
|
||||
ModerationMutedAccounts: undefined
|
||||
ModerationBlockedAccounts: undefined
|
||||
Settings: undefined
|
||||
Profile: {name: string; hideBackButton?: boolean}
|
||||
ProfileFollowers: {name: string}
|
||||
ProfileFollows: {name: string}
|
||||
ProfileList: {name: string; rkey: string}
|
||||
PostThread: {name: string; rkey: string}
|
||||
PostLikedBy: {name: string; rkey: string}
|
||||
PostRepostedBy: {name: string; rkey: string}
|
||||
CustomFeed: {name: string; rkey: string; displayName?: string}
|
||||
Debug: undefined
|
||||
Log: undefined
|
||||
Support: undefined
|
||||
|
@ -22,9 +28,6 @@ export type CommonNavigatorParams = {
|
|||
AppPasswords: undefined
|
||||
SavedFeeds: undefined
|
||||
PinnedFeeds: undefined
|
||||
CustomFeed: {name: string; rkey: string; displayName?: string}
|
||||
MutedAccounts: undefined
|
||||
BlockedAccounts: undefined
|
||||
}
|
||||
|
||||
export type BottomTabNavigatorParams = CommonNavigatorParams & {
|
||||
|
|
|
@ -10,3 +10,18 @@ export function sanitizeDisplayName(str: string): string {
|
|||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
export function combinedDisplayName({
|
||||
handle,
|
||||
displayName,
|
||||
}: {
|
||||
handle?: string
|
||||
displayName?: string
|
||||
}): string {
|
||||
if (!handle) {
|
||||
return ''
|
||||
}
|
||||
return displayName
|
||||
? `${sanitizeDisplayName(displayName)} (@${handle})`
|
||||
: `@${handle}`
|
||||
}
|
||||
|
|
4
src/lib/strings/headings.ts
Normal file
4
src/lib/strings/headings.ts
Normal file
|
@ -0,0 +1,4 @@
|
|||
export function bskyTitle(page: string, unreadCountLabel?: string) {
|
||||
const unreadPrefix = unreadCountLabel ? `(${unreadCountLabel}) ` : ''
|
||||
return `${unreadPrefix}${page} - Bluesky`
|
||||
}
|
|
@ -94,6 +94,15 @@ export function convertBskyAppUrlIfNeeded(url: string): string {
|
|||
return url
|
||||
}
|
||||
|
||||
export function listUriToHref(url: string): string {
|
||||
try {
|
||||
const {hostname, rkey} = new AtUri(url)
|
||||
return `/profile/${hostname}/lists/${rkey}`
|
||||
} catch {
|
||||
return ''
|
||||
}
|
||||
}
|
||||
|
||||
export function getYoutubeVideoId(link: string): string | undefined {
|
||||
let url
|
||||
try {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue