@sleeperhq/mini-core 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -0
- package/app.json +9 -0
- package/index.ts +6 -0
- package/package.json +69 -0
- package/src/components/index.tsx +63 -0
- package/src/dev_server/index.tsx +104 -0
- package/src/packages/index.ts +4 -0
- package/src/types/index.ts +116 -0
package/README.md
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# Sleeper-Mini Core
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+
|
|
5
|
+
## Description
|
|
6
|
+
|
|
7
|
+
This package contains built in sleeper UI components and dev server functionality for creating mini apps.
|
|
8
|
+
|
|
9
|
+
Please see the main repo at [sleeper-mini](https://github.com/blitzstudios/sleeper-mini) for more information.
|
package/app.json
ADDED
package/index.ts
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@sleeperhq/mini-core",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Core library frameworks for developing Sleeper Mini Apps.",
|
|
5
|
+
"main": "index.ts",
|
|
6
|
+
"types": "index.d.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"test": "jest",
|
|
9
|
+
"lint": "eslint ."
|
|
10
|
+
},
|
|
11
|
+
"repository": {
|
|
12
|
+
"type": "git",
|
|
13
|
+
"url": "git+https://github.com/blitzstudios/sleeper-mini-core.git"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"sleeper",
|
|
17
|
+
"mini",
|
|
18
|
+
"apps",
|
|
19
|
+
"fantasy",
|
|
20
|
+
"sports"
|
|
21
|
+
],
|
|
22
|
+
"author": "Luke Herbold <luke@sleeper.app>",
|
|
23
|
+
"license": "MIT",
|
|
24
|
+
"bugs": {
|
|
25
|
+
"url": "https://github.com/blitzstudios/sleeper-mini-core/issues"
|
|
26
|
+
},
|
|
27
|
+
"homepage": "https://github.com/blitzstudios/sleeper-mini-core#readme",
|
|
28
|
+
"publishConfig": {
|
|
29
|
+
"registry": "https://registry.npmjs.org/",
|
|
30
|
+
"access": "public"
|
|
31
|
+
},
|
|
32
|
+
"peerDependencies": {
|
|
33
|
+
"@callstack/repack": "blitzstudios/repack.git#callstack-repack-v2.5.3-gitpkg",
|
|
34
|
+
"@react-native-community/netinfo": "^9.3.7",
|
|
35
|
+
"axios": "0.15.3",
|
|
36
|
+
"lodash": "4.17.21",
|
|
37
|
+
"react": "17.0.2",
|
|
38
|
+
"react-native": "0.66.5",
|
|
39
|
+
"react-native-linear-gradient": "2.5.6",
|
|
40
|
+
"react-native-svg": "13.7.0",
|
|
41
|
+
"react-native-udp": "^4.1.6"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@babel/core": "^7.12.9",
|
|
45
|
+
"@babel/preset-typescript": "^7.16.7",
|
|
46
|
+
"@babel/runtime": "^7.12.5",
|
|
47
|
+
"@react-native-community/eslint-config": "^2.0.0",
|
|
48
|
+
"@types/lodash": "^4.14.182",
|
|
49
|
+
"@types/react-native": "0.66.5",
|
|
50
|
+
"@typescript-eslint/eslint-plugin": "^5.31.0",
|
|
51
|
+
"@typescript-eslint/parser": "^5.31.0",
|
|
52
|
+
"babel-jest": "^26.6.3",
|
|
53
|
+
"babel-loader": "^8.2.5",
|
|
54
|
+
"eslint": "7.14.0",
|
|
55
|
+
"jest": "^26.6.3",
|
|
56
|
+
"metro-react-native-babel-preset": "^0.66.2",
|
|
57
|
+
"react-test-renderer": "17.0.2",
|
|
58
|
+
"terser-webpack-plugin": "^5.3.3",
|
|
59
|
+
"typescript": "^4.7.2",
|
|
60
|
+
"webpack": "^5.73.0"
|
|
61
|
+
},
|
|
62
|
+
"resolutions": {
|
|
63
|
+
"react-devtools-core": "~4.25.0",
|
|
64
|
+
"@pmmmwh/react-refresh-webpack-plugin": "^0.4.3"
|
|
65
|
+
},
|
|
66
|
+
"jest": {
|
|
67
|
+
"preset": "react-native"
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import {
|
|
3
|
+
ColorValue,
|
|
4
|
+
GestureResponderEvent,
|
|
5
|
+
TextProps,
|
|
6
|
+
View,
|
|
7
|
+
ViewStyle,
|
|
8
|
+
} from 'react-native';
|
|
9
|
+
import {Federated} from '@callstack/repack/client';
|
|
10
|
+
import {Context} from '../types';
|
|
11
|
+
|
|
12
|
+
const _SleeperModule = React.lazy(() =>
|
|
13
|
+
Federated.importModule('sleeper', 'index').catch(() => ({
|
|
14
|
+
default: props => {
|
|
15
|
+
console.log(
|
|
16
|
+
`[Sleeper] Failed to load <${props?.component}>. Check connection to the app.`,
|
|
17
|
+
);
|
|
18
|
+
return <View />;
|
|
19
|
+
},
|
|
20
|
+
})),
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
type ButtonProps = {
|
|
24
|
+
height?: number;
|
|
25
|
+
gradient?: (string | number)[];
|
|
26
|
+
start?: {x: number; y: number};
|
|
27
|
+
end?: {x: number; y: number};
|
|
28
|
+
disable?: boolean;
|
|
29
|
+
onPress?: (event: GestureResponderEvent) => void;
|
|
30
|
+
text?: string;
|
|
31
|
+
};
|
|
32
|
+
const Button = (props: ButtonProps) => {
|
|
33
|
+
return (
|
|
34
|
+
<React.Suspense fallback={<View />}>
|
|
35
|
+
<_SleeperModule component="Button" {...props} />
|
|
36
|
+
</React.Suspense>
|
|
37
|
+
);
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const Text = (props: TextProps) => {
|
|
41
|
+
return (
|
|
42
|
+
<React.Suspense fallback={<View />}>
|
|
43
|
+
<_SleeperModule component="Text" {...props} />
|
|
44
|
+
</React.Suspense>
|
|
45
|
+
);
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
type JerseyProps = {
|
|
49
|
+
style: ViewStyle;
|
|
50
|
+
sport: 'nfl' | 'nba' | 'cbb' | 'cfb' | 'mlb';
|
|
51
|
+
number: string;
|
|
52
|
+
fill: ColorValue;
|
|
53
|
+
};
|
|
54
|
+
const Jersey = (props: JerseyProps) => {
|
|
55
|
+
return (
|
|
56
|
+
<React.Suspense fallback={<View />}>
|
|
57
|
+
<_SleeperModule component="Jersey" {...props} />
|
|
58
|
+
</React.Suspense>
|
|
59
|
+
);
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
export type {ButtonProps, Context, TextProps, JerseyProps};
|
|
63
|
+
export {Button, Text, Jersey};
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import React, {useEffect} from 'react';
|
|
2
|
+
import {Platform} from 'react-native';
|
|
3
|
+
import {ScriptManager, Federated} from '@callstack/repack/client';
|
|
4
|
+
import NetInfo from '@react-native-community/netinfo';
|
|
5
|
+
import dgram from 'react-native-udp';
|
|
6
|
+
import {
|
|
7
|
+
localSocketPort,
|
|
8
|
+
remoteSocketPort,
|
|
9
|
+
remoteBundlePort as _remoteBundlePort,
|
|
10
|
+
release,
|
|
11
|
+
remoteIP,
|
|
12
|
+
} from '../../app.json';
|
|
13
|
+
import axios from 'axios';
|
|
14
|
+
|
|
15
|
+
const remoteBundleHost = release ? remoteIP : 'localhost';
|
|
16
|
+
const remoteBundlePort = release ? _remoteBundlePort : 8081;
|
|
17
|
+
|
|
18
|
+
ScriptManager.shared.addResolver(async (scriptId, caller) => {
|
|
19
|
+
const extension =
|
|
20
|
+
scriptId === 'sleeper' ? '.container.bundle' : '.chunk.bundle';
|
|
21
|
+
const resolveURL = Federated.createURLResolver({
|
|
22
|
+
containers: {
|
|
23
|
+
sleeper: `http://${remoteBundleHost}:${remoteBundlePort}/[name]${extension}`,
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
// Try to resolve URL based on scriptId and caller
|
|
28
|
+
const url = resolveURL(scriptId, caller);
|
|
29
|
+
const query = release ? undefined : {platform: Platform.OS};
|
|
30
|
+
|
|
31
|
+
const response = await axios
|
|
32
|
+
.get(url + '?' + new URLSearchParams(query), {method: 'HEAD'})
|
|
33
|
+
.catch(() => ({
|
|
34
|
+
status: 404,
|
|
35
|
+
}));
|
|
36
|
+
|
|
37
|
+
console.log('[Sleeper] load script:', scriptId, caller);
|
|
38
|
+
if (response?.status === 200) {
|
|
39
|
+
return {url, query};
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
const DevServer = props => {
|
|
44
|
+
const onSocket = msg => {
|
|
45
|
+
const json = JSON.parse(msg.toString());
|
|
46
|
+
if (json?._connected) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
props.onContextChanged(json);
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const onError = err => {
|
|
54
|
+
console.error('[Sleeper] Socket error: ' + err);
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const bindSocket = async socket => {
|
|
58
|
+
const netInfo = await NetInfo.fetch();
|
|
59
|
+
const netInfoDetails = netInfo?.details;
|
|
60
|
+
|
|
61
|
+
if (!netInfoDetails || !('ipAddress' in netInfoDetails)) {
|
|
62
|
+
console.error('[Sleeper] Failed to determine local IP address.');
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
socket.bind({port: localSocketPort, address: netInfoDetails.ipAddress});
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const pingServer = socket => {
|
|
70
|
+
// Continue to ping the sleeper app server until it responds.
|
|
71
|
+
return new Promise(() => {
|
|
72
|
+
NetInfo.fetch().then(netInfo => {
|
|
73
|
+
const netInfoDetails = netInfo?.details;
|
|
74
|
+
if (!netInfoDetails || !('ipAddress' in netInfoDetails)) {
|
|
75
|
+
console.error('[Sleeper] Failed to determine local IP address.');
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const json = JSON.stringify({_ip: netInfoDetails.ipAddress});
|
|
80
|
+
|
|
81
|
+
(async function ping() {
|
|
82
|
+
socket.send(json, undefined, undefined, remoteSocketPort, remoteIP);
|
|
83
|
+
setTimeout(ping, 5000);
|
|
84
|
+
})();
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
useEffect(() => {
|
|
90
|
+
const socket = dgram.createSocket({type: 'udp4'});
|
|
91
|
+
bindSocket(socket);
|
|
92
|
+
pingServer(socket);
|
|
93
|
+
|
|
94
|
+
socket.on('message', onSocket);
|
|
95
|
+
socket.on('error', onError);
|
|
96
|
+
return () => {
|
|
97
|
+
socket.removeAllListeners();
|
|
98
|
+
};
|
|
99
|
+
}, []);
|
|
100
|
+
|
|
101
|
+
return <></>;
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
export default DevServer;
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
export type Maybe<T> = T | null;
|
|
2
|
+
export type Scalars = {
|
|
3
|
+
ID: string;
|
|
4
|
+
String: string;
|
|
5
|
+
Boolean: boolean;
|
|
6
|
+
Int: number;
|
|
7
|
+
Float: number;
|
|
8
|
+
Json: {[key: string]: any};
|
|
9
|
+
List: string[];
|
|
10
|
+
Map: {[key: string]: any};
|
|
11
|
+
MapWithSnowflakeKey: {[key: string]: any};
|
|
12
|
+
Set: string[];
|
|
13
|
+
Snowflake: string;
|
|
14
|
+
SnowflakeList: string[];
|
|
15
|
+
SnowflakeSet: string[];
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export type User = {
|
|
19
|
+
__typename?: 'User';
|
|
20
|
+
async_bundles?: Maybe<Scalars['List']>;
|
|
21
|
+
avatar?: Maybe<Scalars['String']>;
|
|
22
|
+
cookies?: Maybe<Scalars['Int']>;
|
|
23
|
+
created?: Maybe<Scalars['Int']>;
|
|
24
|
+
currencies?: Maybe<Scalars['Map']>;
|
|
25
|
+
data_updated?: Maybe<Scalars['Map']>;
|
|
26
|
+
deleted?: Maybe<Scalars['Int']>;
|
|
27
|
+
display_name?: Maybe<Scalars['String']>;
|
|
28
|
+
email?: Maybe<Scalars['String']>;
|
|
29
|
+
is_bot?: Maybe<Scalars['Boolean']>;
|
|
30
|
+
metadata?: Maybe<Scalars['Json']>;
|
|
31
|
+
notifications?: Maybe<Scalars['Map']>;
|
|
32
|
+
pending?: Maybe<Scalars['Boolean']>;
|
|
33
|
+
phone?: Maybe<Scalars['String']>;
|
|
34
|
+
real_name?: Maybe<Scalars['String']>;
|
|
35
|
+
solicitable?: Maybe<Scalars['Boolean']>;
|
|
36
|
+
summoner_name?: Maybe<Scalars['String']>;
|
|
37
|
+
summoner_region?: Maybe<Scalars['String']>;
|
|
38
|
+
token?: Maybe<Scalars['String']>;
|
|
39
|
+
user_id?: Maybe<Scalars['Snowflake']>;
|
|
40
|
+
username?: Maybe<Scalars['String']>;
|
|
41
|
+
verification?: Maybe<Scalars['String']>;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export type League = {
|
|
45
|
+
__typename?: 'League';
|
|
46
|
+
avatar?: Maybe<Scalars['String']>;
|
|
47
|
+
company_id?: Maybe<Scalars['Snowflake']>;
|
|
48
|
+
display_order?: Maybe<Scalars['Int']>;
|
|
49
|
+
draft_id?: Maybe<Scalars['Snowflake']>;
|
|
50
|
+
group_id?: Maybe<Scalars['Snowflake']>;
|
|
51
|
+
last_author_avatar?: Maybe<Scalars['String']>;
|
|
52
|
+
last_author_display_name?: Maybe<Scalars['String']>;
|
|
53
|
+
last_author_id?: Maybe<Scalars['Snowflake']>;
|
|
54
|
+
last_author_is_bot?: Maybe<Scalars['Boolean']>;
|
|
55
|
+
last_message_attachment?: Maybe<Scalars['Json']>;
|
|
56
|
+
last_message_id?: Maybe<Scalars['Snowflake']>;
|
|
57
|
+
last_message_text?: Maybe<Scalars['String']>;
|
|
58
|
+
last_message_text_map?: Maybe<Scalars['Json']>;
|
|
59
|
+
last_message_time?: Maybe<Scalars['Int']>;
|
|
60
|
+
last_pinned_message_id?: Maybe<Scalars['Snowflake']>;
|
|
61
|
+
last_read_id?: Maybe<Scalars['Snowflake']>;
|
|
62
|
+
last_transaction_id?: Maybe<Scalars['Snowflake']>;
|
|
63
|
+
league_id?: Maybe<Scalars['Snowflake']>;
|
|
64
|
+
matchup_legs?: Maybe<Scalars['List']>;
|
|
65
|
+
metadata?: Maybe<Scalars['Map']>;
|
|
66
|
+
name?: Maybe<Scalars['String']>;
|
|
67
|
+
previous_league_id?: Maybe<Scalars['Snowflake']>;
|
|
68
|
+
roster_positions?: Maybe<Scalars['List']>;
|
|
69
|
+
scoring_settings?: Maybe<Scalars['Map']>;
|
|
70
|
+
season?: Maybe<Scalars['String']>;
|
|
71
|
+
season_type?: Maybe<Scalars['String']>;
|
|
72
|
+
settings?: Maybe<Scalars['Map']>;
|
|
73
|
+
sport?: Maybe<Scalars['String']>;
|
|
74
|
+
status?: Maybe<Scalars['String']>;
|
|
75
|
+
total_rosters?: Maybe<Scalars['Int']>;
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
export type NavigationType =
|
|
79
|
+
| 'DM'
|
|
80
|
+
| 'INBOX'
|
|
81
|
+
| 'MY_FEED'
|
|
82
|
+
| 'FRIENDS'
|
|
83
|
+
| 'LEAGUE'
|
|
84
|
+
| 'ASYNC_SPORT'
|
|
85
|
+
| 'CREATE_LEAGUE'
|
|
86
|
+
| 'LEAGUE_SYNC'
|
|
87
|
+
| 'DRAFTBOARDS'
|
|
88
|
+
| 'SOLO_OVER_UNDER'
|
|
89
|
+
| 'CHANNEL'
|
|
90
|
+
| 'MANAGE_CHANNELS';
|
|
91
|
+
|
|
92
|
+
export type NavigationTypeId =
|
|
93
|
+
| 1 // DRAFTBOARDS
|
|
94
|
+
| 2 // CREATE_DM
|
|
95
|
+
| 3 // DM_SCREEN
|
|
96
|
+
| 4 // CREATE_LEAGUE
|
|
97
|
+
| 5 // MY_FEED
|
|
98
|
+
| 6 // MENTIONS
|
|
99
|
+
| 7; // FRIENDS
|
|
100
|
+
|
|
101
|
+
export type Navigation = {
|
|
102
|
+
selectedNavType: NavigationType;
|
|
103
|
+
selectedNavTypeId: NavigationTypeId;
|
|
104
|
+
selectedNavData: {};
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
export type Actions = {
|
|
108
|
+
navigate: (navType?: NavigationType, navTypeId?: NavigationTypeId) => void;
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
export type Context = {
|
|
112
|
+
user?: User;
|
|
113
|
+
league?: League;
|
|
114
|
+
navigation?: Navigation;
|
|
115
|
+
actions?: Actions;
|
|
116
|
+
};
|