Bluesky app fork with some witchin' additions 💫

Implement web toast

+77 -21
-19
public/index.html
··· 36 36 border-radius: 10px; 37 37 box-shadow: 0 5px 10px #0005; 38 38 } 39 - 40 - /* These styles are for src/view/com/util/Toast */ 41 - div[data-toast-container] { 42 - position: fixed; 43 - bottom: 5vh; 44 - right: 5vh; 45 - width: 350px; 46 - padding: 20px; 47 - display: flex; 48 - flex-direction: row; 49 - align-items: center; 50 - background: #fff; 51 - border-radius: 10px; 52 - box-shadow: 0 5px 10px #0005; 53 - } 54 - div[data-toast-container] > div { 55 - font-size: 18px; 56 - margin-left: 10px; 57 - } 58 39 </style> 59 40 </head> 60 41 <body>
+2 -2
src/App.web.tsx
··· 3 3 import * as view from './view/index' 4 4 import {RootStoreModel, setupState, RootStoreProvider} from './state' 5 5 import {WebShell} from './view/shell/web' 6 - // import Toast from 'react-native-root-toast' TODO 6 + import {ToastContainer} from './view/com/util/Toast.web' 7 7 8 8 function App() { 9 9 const [rootStore, setRootStore] = useState<RootStoreModel | undefined>( ··· 26 26 <SafeAreaProvider> 27 27 <WebShell /> 28 28 </SafeAreaProvider> 29 + <ToastContainer /> 29 30 </RootStoreProvider> 30 31 ) 31 - // <Toast.ToastContainer /> TODO 32 32 } 33 33 34 34 export default App
+75
src/view/com/util/Toast.web.tsx
··· 1 + /* 2 + * Note: the dataSet properties are used to leverage custom CSS in public/index.html 3 + */ 4 + 5 + import React, {useState, useEffect} from 'react' 6 + import {StyleSheet, Text, View} from 'react-native' 7 + import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' 8 + 9 + const DURATION = 3500 10 + 11 + interface ActiveToast { 12 + text: string 13 + } 14 + type GlobalSetActiveToast = (_activeToast: ActiveToast | undefined) => void 15 + 16 + // globals 17 + // = 18 + let globalSetActiveToast: GlobalSetActiveToast | undefined 19 + let toastTimeout: NodeJS.Timeout | undefined 20 + 21 + // components 22 + // = 23 + type ToastContainerProps = {} 24 + export const ToastContainer: React.FC<ToastContainerProps> = ({}) => { 25 + const [activeToast, setActiveToast] = useState<ActiveToast | undefined>() 26 + useEffect(() => { 27 + globalSetActiveToast = (t: ActiveToast | undefined) => { 28 + setActiveToast(t) 29 + } 30 + }) 31 + return ( 32 + <> 33 + {activeToast && ( 34 + <View style={styles.container}> 35 + <FontAwesomeIcon icon="check" size={24} style={styles.icon} /> 36 + <Text style={styles.text}>{activeToast.text}</Text> 37 + </View> 38 + )} 39 + </> 40 + ) 41 + } 42 + 43 + // methods 44 + // = 45 + export function show(text: string) { 46 + if (toastTimeout) { 47 + clearTimeout(toastTimeout) 48 + } 49 + globalSetActiveToast?.({text}) 50 + toastTimeout = setTimeout(() => { 51 + globalSetActiveToast?.(undefined) 52 + }, DURATION) 53 + } 54 + 55 + const styles = StyleSheet.create({ 56 + container: { 57 + position: 'absolute', 58 + right: 20, 59 + bottom: 20, 60 + width: 350, 61 + padding: 20, 62 + flexDirection: 'row', 63 + alignItems: 'center', 64 + backgroundColor: '#000c', 65 + borderRadius: 10, 66 + }, 67 + icon: { 68 + color: '#fff', 69 + }, 70 + text: { 71 + color: '#fff', 72 + fontSize: 18, 73 + marginLeft: 10, 74 + }, 75 + })