1import { Platform } from '@unimodules/core';
2import { LinearGradient } from 'expo-linear-gradient';
3import * as MediaLibrary from 'expo-media-library';
4import * as Permissions from 'expo-permissions';
5import React from 'react';
6import { Dimensions, Image, ScrollView, StyleSheet, Text, View } from 'react-native';
7import { captureRef as takeSnapshotAsync, captureScreen } from 'react-native-view-shot';
8
9import Button from '../components/Button';
10
11// Source: https://codepen.io/zessx/pen/rDEAl <3
12const gradientColors = ['#90dffe', '#38a3d1'];
13
14interface State {
15  image?: string;
16  screenUri?: string;
17}
18
19// See: https://github.com/expo/expo/pull/10229#discussion_r490961694
20// eslint-disable-next-line @typescript-eslint/ban-types
21export default class ViewShotScreen extends React.Component<{}, State> {
22  static navigationOptions = {
23    title: 'ViewShot',
24  };
25
26  readonly state: State = {};
27  view?: View;
28
29  handleRef = (ref: View) => {
30    this.view = ref;
31  };
32
33  handlePress = async () => {
34    try {
35      const image = await takeSnapshotAsync(this.view!, {
36        format: 'png',
37        quality: 0.5,
38        result: 'data-uri',
39      });
40      this.setState({ image });
41    } catch (e) {
42      console.error(e);
43    }
44  };
45
46  handleScreenCapturePress = async () => {
47    if (Platform.OS === 'web') {
48      try {
49        const screenUri = await takeSnapshotAsync((undefined as unknown) as number, {
50          format: 'jpg',
51          quality: 0.8,
52          result: 'data-uri',
53        });
54        this.setState({ screenUri });
55      } catch (e) {
56        console.error(e);
57      }
58      return;
59    }
60    const uri = await captureScreen({
61      format: 'jpg',
62      quality: 0.8,
63    });
64    this.setState({ screenUri: uri });
65  };
66
67  handleAddToMediaLibraryPress = async () => {
68    const uri = this.state.screenUri;
69
70    if (uri) {
71      const { status } = await Permissions.askAsync(Permissions.MEDIA_LIBRARY);
72
73      if (status === 'granted') {
74        await MediaLibrary.createAssetAsync(uri);
75        alert('Successfully added captured screen to media library');
76      } else {
77        alert('Media library permissions not granted');
78      }
79    }
80  };
81
82  render() {
83    const imageSource = { uri: this.state.image };
84    return (
85      <ScrollView contentContainerStyle={{ alignItems: 'center' }}>
86        <View style={styles.snapshotContainer} ref={this.handleRef} collapsable={false}>
87          <LinearGradient
88            colors={gradientColors}
89            style={styles.gradient}
90            start={[0, 0]}
91            end={[0, 1]}>
92            <Image style={styles.snapshot} source={imageSource} />
93            <Text style={styles.text}>Snapshot will show above</Text>
94          </LinearGradient>
95        </View>
96        <Button style={styles.button} onPress={this.handlePress} title="TAKE THE (SNAP)SHOT!" />
97        <Button
98          style={styles.button}
99          onPress={this.handleScreenCapturePress}
100          title="Capture whole screen"
101        />
102        <Image
103          style={{
104            width: Dimensions.get('window').width,
105            height: Dimensions.get('window').height,
106            borderColor: '#f00',
107            borderWidth: 10,
108          }}
109          source={{ uri: this.state.screenUri }}
110        />
111        <Button
112          style={styles.button}
113          disabled={!this.state.screenUri}
114          onPress={this.handleAddToMediaLibraryPress}
115          title="Add to media library"
116        />
117      </ScrollView>
118    );
119  }
120}
121
122const styles = StyleSheet.create({
123  snapshotContainer: {
124    height: 200,
125    alignSelf: 'stretch',
126    alignItems: 'stretch',
127    justifyContent: 'space-around',
128  },
129  gradient: {
130    flex: 1,
131    alignItems: 'center',
132  },
133  snapshot: {
134    width: 150,
135    height: 150,
136  },
137  text: {
138    margin: 10,
139    color: '#fff',
140    fontWeight: '700',
141  },
142  button: {
143    margin: 15,
144  },
145});
146