@messenger-box/slack-ui-browser 10.0.3-alpha.176
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/CHANGELOG.md +8 -0
- package/LICENSE +21 -0
- package/lib/components/Home/Channels.js +62 -0
- package/lib/components/Home/Channels.js.map +1 -0
- package/lib/components/Home/DirectChannels.js +92 -0
- package/lib/components/Home/DirectChannels.js.map +1 -0
- package/lib/components/Home/InviteMembers.js +70 -0
- package/lib/components/Home/InviteMembers.js.map +1 -0
- package/lib/components/Home/Teams.js +62 -0
- package/lib/components/Home/Teams.js.map +1 -0
- package/lib/components/Home/TopCommonSlider.js +35 -0
- package/lib/components/Home/TopCommonSlider.js.map +1 -0
- package/lib/compute.js +223 -0
- package/lib/compute.js.map +1 -0
- package/lib/constants/routes.js +63 -0
- package/lib/constants/routes.js.map +1 -0
- package/lib/hooks/useOptimizedChannelsQueries.js +107 -0
- package/lib/hooks/useOptimizedChannelsQueries.js.map +1 -0
- package/lib/hooks/useRouteState.js +193 -0
- package/lib/hooks/useRouteState.js.map +1 -0
- package/lib/index.js +1 -0
- package/lib/index.js.map +1 -0
- package/lib/machines/routeMachine.js +804 -0
- package/lib/machines/routeMachine.js.map +1 -0
- package/lib/module.js +3 -0
- package/lib/module.js.map +1 -0
- package/lib/queries/slackuiQueries.js +144 -0
- package/lib/queries/slackuiQueries.js.map +1 -0
- package/lib/routes.json +260 -0
- package/lib/screens/Home/HomeScreen.js +664 -0
- package/lib/screens/Home/HomeScreen.js.map +1 -0
- package/lib/screens/Home/index.js +1 -0
- package/lib/screens/Home/index.js.map +1 -0
- package/package.json +52 -0
- package/rollup.config.mjs +41 -0
- package/src/components/Home/Channels.tsx +135 -0
- package/src/components/Home/DirectChannels.tsx +185 -0
- package/src/components/Home/InviteMembers.tsx +134 -0
- package/src/components/Home/Teams.tsx +129 -0
- package/src/components/Home/TopCommonSlider.tsx +70 -0
- package/src/components/Home/index.ts +5 -0
- package/src/components/index.ts +1 -0
- package/src/compute.ts +156 -0
- package/src/constants/index.ts +1 -0
- package/src/constants/routes.ts +92 -0
- package/src/hooks/index.ts +3 -0
- package/src/hooks/useOptimizedChannelsQueries.ts +165 -0
- package/src/hooks/useRouteState.ts +253 -0
- package/src/icons.ts +137 -0
- package/src/index.ts +11 -0
- package/src/machines/index.ts +9 -0
- package/src/machines/routeMachine.ts +682 -0
- package/src/module.tsx +6 -0
- package/src/queries/index.ts +1 -0
- package/src/queries/slackuiQueries.ts +227 -0
- package/src/screens/Home/HomeScreen.tsx +1308 -0
- package/src/screens/Home/index.ts +4 -0
- package/src/screens/NewChannel/NewChannelScreen.tsx +188 -0
- package/src/screens/NewChannel/index.ts +2 -0
- package/src/screens/index.ts +1 -0
- package/tsconfig.json +21 -0
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import React, { useState, useCallback } from 'react';
|
|
2
|
+
import { useNavigate, useLocation } from '@remix-run/react';
|
|
3
|
+
import { FiX, FiHash, FiLock } from '../../icons';
|
|
4
|
+
import { slackUiRoutePaths } from '../../constants/routes';
|
|
5
|
+
|
|
6
|
+
interface NewChannelScreenProps {
|
|
7
|
+
onClose?: () => void;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const NewChannelScreen: React.FC<NewChannelScreenProps> = ({ onClose }) => {
|
|
11
|
+
const navigate = useNavigate();
|
|
12
|
+
const location = useLocation();
|
|
13
|
+
const orgSlug = location.pathname.split('/')[2];
|
|
14
|
+
|
|
15
|
+
const [channelName, setChannelName] = useState('');
|
|
16
|
+
const [description, setDescription] = useState('');
|
|
17
|
+
const [isPrivate, setIsPrivate] = useState(false);
|
|
18
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
19
|
+
const [error, setError] = useState<string | null>(null);
|
|
20
|
+
|
|
21
|
+
const handleClose = useCallback(() => {
|
|
22
|
+
if (onClose) {
|
|
23
|
+
onClose();
|
|
24
|
+
} else {
|
|
25
|
+
navigate(slackUiRoutePaths.home(orgSlug));
|
|
26
|
+
}
|
|
27
|
+
}, [navigate, orgSlug, onClose]);
|
|
28
|
+
|
|
29
|
+
const handleSubmit = useCallback(async (e: React.FormEvent) => {
|
|
30
|
+
e.preventDefault();
|
|
31
|
+
|
|
32
|
+
if (!channelName.trim()) {
|
|
33
|
+
setError('Channel name is required');
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Validate channel name format (lowercase, no spaces, alphanumeric with hyphens)
|
|
38
|
+
const validName = channelName.toLowerCase().replace(/\s+/g, '-').replace(/[^a-z0-9-]/g, '');
|
|
39
|
+
if (validName !== channelName) {
|
|
40
|
+
setChannelName(validName);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
setIsLoading(true);
|
|
44
|
+
setError(null);
|
|
45
|
+
|
|
46
|
+
try {
|
|
47
|
+
// TODO: Implement actual channel creation mutation
|
|
48
|
+
console.log('Creating channel:', { name: channelName, description, isPrivate });
|
|
49
|
+
|
|
50
|
+
// Navigate to the new channel after creation
|
|
51
|
+
// For now, just go back to home
|
|
52
|
+
handleClose();
|
|
53
|
+
} catch (err) {
|
|
54
|
+
setError('Failed to create channel. Please try again.');
|
|
55
|
+
} finally {
|
|
56
|
+
setIsLoading(false);
|
|
57
|
+
}
|
|
58
|
+
}, [channelName, description, isPrivate, handleClose]);
|
|
59
|
+
|
|
60
|
+
const handleChannelNameChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
61
|
+
// Auto-format: lowercase, replace spaces with hyphens
|
|
62
|
+
const value = e.target.value.toLowerCase().replace(/\s+/g, '-');
|
|
63
|
+
setChannelName(value);
|
|
64
|
+
setError(null);
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
return (
|
|
68
|
+
<div className="flex flex-col h-full bg-white">
|
|
69
|
+
{/* Header */}
|
|
70
|
+
<div className="flex items-center justify-between px-4 py-3 border-b border-gray-200">
|
|
71
|
+
<button
|
|
72
|
+
onClick={handleClose}
|
|
73
|
+
className="p-2 hover:bg-gray-100 rounded-full transition-colors"
|
|
74
|
+
>
|
|
75
|
+
<FiX className="w-5 h-5 text-gray-600" />
|
|
76
|
+
</button>
|
|
77
|
+
<h1 className="text-lg font-semibold text-gray-900">Create Channel</h1>
|
|
78
|
+
<button
|
|
79
|
+
onClick={handleSubmit}
|
|
80
|
+
disabled={isLoading || !channelName.trim()}
|
|
81
|
+
className="px-4 py-2 bg-blue-600 text-white rounded-md text-sm font-medium hover:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
|
|
82
|
+
>
|
|
83
|
+
{isLoading ? 'Creating...' : 'Create'}
|
|
84
|
+
</button>
|
|
85
|
+
</div>
|
|
86
|
+
|
|
87
|
+
{/* Form */}
|
|
88
|
+
<form onSubmit={handleSubmit} className="flex-1 overflow-y-auto p-4 space-y-6">
|
|
89
|
+
{/* Channel Type Toggle */}
|
|
90
|
+
<div className="space-y-2">
|
|
91
|
+
<label className="block text-sm font-medium text-gray-700">Channel Type</label>
|
|
92
|
+
<div className="flex gap-4">
|
|
93
|
+
<button
|
|
94
|
+
type="button"
|
|
95
|
+
onClick={() => setIsPrivate(false)}
|
|
96
|
+
className={`flex items-center gap-2 px-4 py-3 rounded-lg border-2 transition-colors ${
|
|
97
|
+
!isPrivate
|
|
98
|
+
? 'border-blue-500 bg-blue-50 text-blue-700'
|
|
99
|
+
: 'border-gray-200 bg-white text-gray-700 hover:border-gray-300'
|
|
100
|
+
}`}
|
|
101
|
+
>
|
|
102
|
+
<FiHash className="w-5 h-5" />
|
|
103
|
+
<div className="text-left">
|
|
104
|
+
<div className="font-medium">Public</div>
|
|
105
|
+
<div className="text-xs text-gray-500">Anyone can join</div>
|
|
106
|
+
</div>
|
|
107
|
+
</button>
|
|
108
|
+
<button
|
|
109
|
+
type="button"
|
|
110
|
+
onClick={() => setIsPrivate(true)}
|
|
111
|
+
className={`flex items-center gap-2 px-4 py-3 rounded-lg border-2 transition-colors ${
|
|
112
|
+
isPrivate
|
|
113
|
+
? 'border-blue-500 bg-blue-50 text-blue-700'
|
|
114
|
+
: 'border-gray-200 bg-white text-gray-700 hover:border-gray-300'
|
|
115
|
+
}`}
|
|
116
|
+
>
|
|
117
|
+
<FiLock className="w-5 h-5" />
|
|
118
|
+
<div className="text-left">
|
|
119
|
+
<div className="font-medium">Private</div>
|
|
120
|
+
<div className="text-xs text-gray-500">Invite only</div>
|
|
121
|
+
</div>
|
|
122
|
+
</button>
|
|
123
|
+
</div>
|
|
124
|
+
</div>
|
|
125
|
+
|
|
126
|
+
{/* Channel Name */}
|
|
127
|
+
<div className="space-y-2">
|
|
128
|
+
<label htmlFor="channelName" className="block text-sm font-medium text-gray-700">
|
|
129
|
+
Channel Name
|
|
130
|
+
</label>
|
|
131
|
+
<div className="relative">
|
|
132
|
+
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
|
133
|
+
{isPrivate ? (
|
|
134
|
+
<FiLock className="w-5 h-5 text-gray-400" />
|
|
135
|
+
) : (
|
|
136
|
+
<FiHash className="w-5 h-5 text-gray-400" />
|
|
137
|
+
)}
|
|
138
|
+
</div>
|
|
139
|
+
<input
|
|
140
|
+
id="channelName"
|
|
141
|
+
type="text"
|
|
142
|
+
value={channelName}
|
|
143
|
+
onChange={handleChannelNameChange}
|
|
144
|
+
placeholder="e.g. marketing, engineering"
|
|
145
|
+
className="w-full pl-10 pr-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-colors"
|
|
146
|
+
maxLength={80}
|
|
147
|
+
/>
|
|
148
|
+
</div>
|
|
149
|
+
<p className="text-xs text-gray-500">
|
|
150
|
+
Lowercase letters, numbers, and hyphens only. Max 80 characters.
|
|
151
|
+
</p>
|
|
152
|
+
{error && <p className="text-xs text-red-500">{error}</p>}
|
|
153
|
+
</div>
|
|
154
|
+
|
|
155
|
+
{/* Description */}
|
|
156
|
+
<div className="space-y-2">
|
|
157
|
+
<label htmlFor="description" className="block text-sm font-medium text-gray-700">
|
|
158
|
+
Description <span className="text-gray-400">(optional)</span>
|
|
159
|
+
</label>
|
|
160
|
+
<textarea
|
|
161
|
+
id="description"
|
|
162
|
+
value={description}
|
|
163
|
+
onChange={(e) => setDescription(e.target.value)}
|
|
164
|
+
placeholder="What's this channel about?"
|
|
165
|
+
rows={3}
|
|
166
|
+
className="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-colors resize-none"
|
|
167
|
+
maxLength={250}
|
|
168
|
+
/>
|
|
169
|
+
<p className="text-xs text-gray-500">
|
|
170
|
+
{description.length}/250 characters
|
|
171
|
+
</p>
|
|
172
|
+
</div>
|
|
173
|
+
|
|
174
|
+
{/* Info */}
|
|
175
|
+
<div className="bg-gray-50 rounded-lg p-4">
|
|
176
|
+
<h3 className="text-sm font-medium text-gray-700 mb-2">What happens next?</h3>
|
|
177
|
+
<ul className="text-sm text-gray-600 space-y-1">
|
|
178
|
+
<li>• You'll be added as the channel admin</li>
|
|
179
|
+
<li>• You can invite members after creating the channel</li>
|
|
180
|
+
{isPrivate && <li>• Only invited members can see and join this channel</li>}
|
|
181
|
+
</ul>
|
|
182
|
+
</div>
|
|
183
|
+
</form>
|
|
184
|
+
</div>
|
|
185
|
+
);
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
export default React.memo(NewChannelScreen);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './Home';
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "../../../tsconfig.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"rootDir": "src",
|
|
5
|
+
"outDir": "lib",
|
|
6
|
+
"declaration": true,
|
|
7
|
+
"declarationDir": "lib",
|
|
8
|
+
"types": ["@types/node", "@types/jest", "../../../typings"],
|
|
9
|
+
"skipLibCheck": true,
|
|
10
|
+
"jsx": "react-jsx"
|
|
11
|
+
},
|
|
12
|
+
"include": ["src"],
|
|
13
|
+
"exclude": [
|
|
14
|
+
"**/*.test.ts",
|
|
15
|
+
"../../../node_modules",
|
|
16
|
+
"node_modules",
|
|
17
|
+
"lib",
|
|
18
|
+
"webpack.config.js",
|
|
19
|
+
"rollup.config.mjs"
|
|
20
|
+
]
|
|
21
|
+
}
|