weechatRN

Weechat relay client for iOS using websockets https://github.com/mhoran/weechatRN
git clone http://git.hanabi.in/repos/weechatRN.git
Log | Files | Refs | README | LICENSE

App.tsx (6477B)


      1 import * as React from 'react';
      2 import {
      3   SafeAreaView,
      4   View,
      5   Text,
      6   Image,
      7   TouchableOpacity,
      8   StyleSheet,
      9   Keyboard,
     10   Dimensions,
     11   Platform
     12 } from 'react-native';
     13 import { connect, ConnectedProps } from 'react-redux';
     14 
     15 import DrawerLayout from 'react-native-drawer-layout-polyfill';
     16 
     17 import BufferGate from './buffers/ui/BufferGate';
     18 import BufferList from './buffers/ui/BufferList';
     19 import { StoreState } from '../store';
     20 import { registerForPushNotificationsAsync } from '../lib/helpers/push-notifications';
     21 
     22 const connector = connect((state: StoreState) => {
     23   const currentBufferId = state.app.currentBufferId;
     24   const currentBuffer =
     25     (currentBufferId && state.buffers[currentBufferId]) || null;
     26 
     27   const numHighlights = Object.values(state.hotlists).reduce(
     28     (sum, hlist) => sum + hlist.highlight,
     29     0
     30   );
     31 
     32   return {
     33     buffers: state.buffers,
     34     currentBufferId: currentBuffer && currentBufferId,
     35     currentBuffer,
     36     hasHighlights: numHighlights > 0
     37   };
     38 });
     39 
     40 type PropsFromRedux = ConnectedProps<typeof connector>;
     41 
     42 type Props = PropsFromRedux & {
     43   disconnect: () => void;
     44   fetchBufferInfo: (bufferId: string) => void;
     45   sendMessageToBuffer: (fullBufferName: string, message: string) => void;
     46   clearHotlistForBuffer: (fullBufferName: string) => void;
     47 };
     48 
     49 interface State {
     50   showTopic: boolean;
     51   drawerWidth: number;
     52 }
     53 
     54 class App extends React.Component<Props, State> {
     55   drawer: DrawerLayout;
     56 
     57   drawerWidth = () => {
     58     /*
     59      * Default drawer width is screen width - header height
     60      * with a max width of 280 on mobile and 320 on tablet
     61      * https://material.io/guidelines/patterns/navigation-drawer.html
     62      */
     63     const { height, width } = Dimensions.get('window');
     64     const smallerAxisSize = Math.min(height, width);
     65     const isLandscape = width > height;
     66     const isTablet = smallerAxisSize >= 600;
     67     const appBarHeight = Platform.OS === 'ios' ? (isLandscape ? 32 : 44) : 56;
     68     const maxWidth = isTablet ? 320 : 280;
     69 
     70     return Math.min(smallerAxisSize - appBarHeight, maxWidth);
     71   };
     72 
     73   state: State = {
     74     showTopic: false,
     75     drawerWidth: this.drawerWidth()
     76   };
     77 
     78   changeCurrentBuffer = (buffer: WeechatBuffer) => {
     79     const { currentBufferId, fetchBufferInfo } = this.props;
     80 
     81     this.drawer.closeDrawer();
     82     if (currentBufferId !== buffer.id) {
     83       this.props.dispatch({
     84         type: 'CHANGE_CURRENT_BUFFER',
     85         bufferId: buffer.id
     86       });
     87       this.props.clearHotlistForBuffer(buffer.full_name);
     88       fetchBufferInfo(buffer.id);
     89     }
     90   };
     91 
     92   toggleShowTopic = () => {
     93     this.setState((state) => ({
     94       showTopic: !state.showTopic
     95     }));
     96   };
     97 
     98   openDrawer = () => {
     99     this.drawer.openDrawer();
    100     Keyboard.dismiss();
    101   };
    102 
    103   sendMessage = (message: string) => {
    104     const { currentBuffer, sendMessageToBuffer } = this.props;
    105 
    106     sendMessageToBuffer(currentBuffer.full_name, message);
    107   };
    108 
    109   updateWidth = () => {
    110     if (this.state.drawerWidth !== this.drawerWidth()) {
    111       this.setState({ drawerWidth: this.drawerWidth() });
    112     }
    113   };
    114 
    115   componentDidMount() {
    116     Dimensions.addEventListener('change', this.updateWidth);
    117 
    118     const { currentBufferId, fetchBufferInfo } = this.props;
    119     if (currentBufferId) {
    120       fetchBufferInfo(currentBufferId);
    121     } else {
    122       this.drawer.openDrawer();
    123     }
    124 
    125     registerForPushNotificationsAsync();
    126   }
    127 
    128   componentWillUnmount() {
    129     Dimensions.removeEventListener('change', this.updateWidth);
    130   }
    131 
    132   componentDidUpdate(prevProps: Props) {
    133     const { currentBufferId } = this.props;
    134     if (currentBufferId !== prevProps.currentBufferId && !currentBufferId) {
    135       this.drawer.openDrawer();
    136     }
    137   }
    138 
    139   render() {
    140     const {
    141       buffers,
    142       currentBufferId,
    143       currentBuffer,
    144       hasHighlights
    145     } = this.props;
    146 
    147     const { showTopic, drawerWidth } = this.state;
    148 
    149     const sidebar = () => (
    150       <BufferList
    151         buffers={Object.values(buffers).sort((a, b) => a.number - b.number)}
    152         currentBufferId={currentBufferId}
    153         onSelectBuffer={this.changeCurrentBuffer}
    154       />
    155     );
    156 
    157     return (
    158       <View style={styles.container}>
    159         <DrawerLayout
    160           ref={(d: DrawerLayout) => (this.drawer = d)}
    161           renderNavigationView={sidebar}
    162           keyboardDismissMode={'on-drag'}
    163           drawerWidth={drawerWidth}
    164           useNativeAnimations={true}
    165         >
    166           <SafeAreaView style={styles.container}>
    167             <View style={styles.topbar}>
    168               <View style={styles.channels}>
    169                 <TouchableOpacity
    170                   style={styles.channelsButton}
    171                   onPress={this.openDrawer}
    172                 >
    173                   <Text
    174                     style={[
    175                       styles.channelsButtonText,
    176                       hasHighlights && {
    177                         color: '#ffcf7f'
    178                       }
    179                     ]}
    180                   >
    181                     #
    182                   </Text>
    183                 </TouchableOpacity>
    184               </View>
    185               <TouchableOpacity onPress={this.toggleShowTopic}>
    186                 <Text style={styles.topbarText}>
    187                   {currentBuffer && currentBuffer.short_name}
    188                 </Text>
    189               </TouchableOpacity>
    190               <View style={styles.channels}>
    191                 <TouchableOpacity
    192                   style={styles.channelsButton}
    193                   onPress={this.props.disconnect}
    194                 >
    195                   <Image source={require('./icons/eject.png')} />
    196                 </TouchableOpacity>
    197               </View>
    198             </View>
    199             <BufferGate
    200               showTopic={showTopic}
    201               sendMessage={this.sendMessage}
    202               bufferId={currentBufferId}
    203             />
    204           </SafeAreaView>
    205         </DrawerLayout>
    206       </View>
    207     );
    208   }
    209 }
    210 
    211 export default connector(App);
    212 
    213 const styles = StyleSheet.create({
    214   topbar: {
    215     flexDirection: 'row',
    216     backgroundColor: '#333',
    217     justifyContent: 'space-between',
    218     alignItems: 'center',
    219     paddingBottom: 10
    220   },
    221   channels: {
    222     paddingHorizontal: 5
    223   },
    224   channelsButton: {
    225     paddingVertical: 5,
    226     paddingHorizontal: 10,
    227     width: 40
    228   },
    229   channelsButtonText: {
    230     textAlign: 'center',
    231     fontSize: 20,
    232     fontFamily: 'Gill Sans',
    233     color: '#eee',
    234     fontWeight: 'bold'
    235   },
    236   topbarText: {
    237     color: '#eee',
    238     fontFamily: 'Thonburi',
    239     fontWeight: 'bold',
    240     fontSize: 15
    241   },
    242   container: {
    243     flex: 1,
    244     backgroundColor: '#333'
    245   }
    246 });