feat: implement live channel sidebar
This commit is contained in:
		
							
								
								
									
										8
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										8
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							@@ -13,7 +13,7 @@
 | 
				
			|||||||
				"@fortawesome/free-solid-svg-icons": "^6.2.1",
 | 
									"@fortawesome/free-solid-svg-icons": "^6.2.1",
 | 
				
			||||||
				"@fortawesome/svelte-fontawesome": "^0.2.0",
 | 
									"@fortawesome/svelte-fontawesome": "^0.2.0",
 | 
				
			||||||
				"detect-it": "^4.0.1",
 | 
									"detect-it": "^4.0.1",
 | 
				
			||||||
				"fedwave-chat-client": "^0.0.1",
 | 
									"fedwave-chat-client": "^0.0.2",
 | 
				
			||||||
				"jose": "^4.11.2",
 | 
									"jose": "^4.11.2",
 | 
				
			||||||
				"svrollbar": "^0.12.0"
 | 
									"svrollbar": "^0.12.0"
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
@@ -1605,9 +1605,9 @@
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"node_modules/fedwave-chat-client": {
 | 
							"node_modules/fedwave-chat-client": {
 | 
				
			||||||
			"version": "0.0.1",
 | 
								"version": "0.0.2",
 | 
				
			||||||
			"resolved": "https://registry.npmjs.org/fedwave-chat-client/-/fedwave-chat-client-0.0.1.tgz",
 | 
								"resolved": "https://registry.npmjs.org/fedwave-chat-client/-/fedwave-chat-client-0.0.2.tgz",
 | 
				
			||||||
			"integrity": "sha512-VP7OJELCx1V0c26QbgF+mLoMq4EyPLbrjIylAvS12hv2XVlmGJRqnrh6kCVHqSr6ZwirjlM/eLSDgJ2jWXMiyw==",
 | 
								"integrity": "sha512-r4rvOGVEvb09LrIrWOF4D1yEd/9duxameFlnJltOZxXagPkzS5RhJagIOKO0L+37moUHOVv/cMu3Gg+YFDA0Pg==",
 | 
				
			||||||
			"dependencies": {
 | 
								"dependencies": {
 | 
				
			||||||
				"socket.io-client": "^3.1.3"
 | 
									"socket.io-client": "^3.1.3"
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -38,7 +38,7 @@
 | 
				
			|||||||
		"@fortawesome/free-solid-svg-icons": "^6.2.1",
 | 
							"@fortawesome/free-solid-svg-icons": "^6.2.1",
 | 
				
			||||||
		"@fortawesome/svelte-fontawesome": "^0.2.0",
 | 
							"@fortawesome/svelte-fontawesome": "^0.2.0",
 | 
				
			||||||
		"detect-it": "^4.0.1",
 | 
							"detect-it": "^4.0.1",
 | 
				
			||||||
		"fedwave-chat-client": "^0.0.1",
 | 
							"fedwave-chat-client": "^0.0.2",
 | 
				
			||||||
		"jose": "^4.11.2",
 | 
							"jose": "^4.11.2",
 | 
				
			||||||
		"svrollbar": "^0.12.0"
 | 
							"svrollbar": "^0.12.0"
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,7 +5,7 @@
 | 
				
			|||||||
 export let size: string = 'var(--avatar-size)';
 | 
					 export let size: string = 'var(--avatar-size)';
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<button class="avatar pa-0 border-0" style:--color="{color}" style:--size="{size}" on:click>
 | 
					<button class="avatar pa-0 border-0 shrink-0" style:--color="{color}" style:--size="{size}" on:click>
 | 
				
			||||||
    <img src="{avatar}" alt="{`${username}'s avatar`}">
 | 
					    <img src="{avatar}" alt="{`${username}'s avatar`}">
 | 
				
			||||||
</button>
 | 
					</button>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,19 +3,54 @@
 | 
				
			|||||||
 export let mobile = false;
 | 
					 export let mobile = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 import { FontAwesomeIcon as FA } from '@fortawesome/svelte-fontawesome';
 | 
					 import { FontAwesomeIcon as FA } from '@fortawesome/svelte-fontawesome';
 | 
				
			||||||
 import { faHome, faPerson } from '@fortawesome/free-solid-svg-icons';
 | 
					 import { faHome, faPerson, faCircle } from '@fortawesome/free-solid-svg-icons';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 import { channels, room } from './chat';
 | 
				
			||||||
 | 
					 import Avatar from './Avatar.svelte';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 let selected = "channels";
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<div class="sidebar h-100vh d-flex" bind:clientWidth class:mobile="{mobile}">
 | 
					<div class="sidebar h-100vh d-flex" bind:clientWidth class:mobile="{mobile}">
 | 
				
			||||||
    <div class="overlay" />
 | 
					    <div class="overlay" />
 | 
				
			||||||
    <div class="servers d-flex col">
 | 
					    <div class="servers shrink-0 d-flex col">
 | 
				
			||||||
	<button class="selected">
 | 
						<button
 | 
				
			||||||
 | 
						    class:selected="{selected === 'whispers'}"
 | 
				
			||||||
 | 
						    on:click="{() => selected = 'whispers'}">
 | 
				
			||||||
	    <FA size="2x" icon="{faPerson}" />
 | 
						    <FA size="2x" icon="{faPerson}" />
 | 
				
			||||||
	</button>
 | 
						</button>
 | 
				
			||||||
	<button>
 | 
						<button
 | 
				
			||||||
 | 
						    class:selected="{selected === 'channels'}"
 | 
				
			||||||
 | 
						    on:click="{() => selected = 'channels'}">
 | 
				
			||||||
	    <FA size="2x" icon="{faHome}" />
 | 
						    <FA size="2x" icon="{faHome}" />
 | 
				
			||||||
	</button>
 | 
						</button>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
 | 
					    {#if selected === 'whispers'}
 | 
				
			||||||
 | 
						<div class="whispers grow-1 mw-0"></div>
 | 
				
			||||||
 | 
					    {:else if selected === 'channels'}
 | 
				
			||||||
 | 
						<div class="channels grow-1 d-flex col mw-0 py-8">
 | 
				
			||||||
 | 
						    {#each $channels.sort((a, b) => b.viewCount - a.viewCount) as channel}
 | 
				
			||||||
 | 
							<a href="{channel.name}"
 | 
				
			||||||
 | 
							   class="channel d-flex align-center mb-8 mx-8 pa-6 radius-12"
 | 
				
			||||||
 | 
							   class:selected="{$room === channel.name.toLowerCase()}">
 | 
				
			||||||
 | 
							    <Avatar username="{channel.name}"
 | 
				
			||||||
 | 
								    avatar="{channel.avatar}" />
 | 
				
			||||||
 | 
							    <div class="channel-name mw-0 ellipsis ml-8">
 | 
				
			||||||
 | 
								{channel.name}
 | 
				
			||||||
 | 
							    </div>
 | 
				
			||||||
 | 
							    {#if false && channel.live}
 | 
				
			||||||
 | 
								<div class="channel-live ml-2 mt-8 color-red">
 | 
				
			||||||
 | 
								    <FA icon="{faCircle}" size="xs" />
 | 
				
			||||||
 | 
								</div>
 | 
				
			||||||
 | 
							    {/if}
 | 
				
			||||||
 | 
							    <div class="spacer"/>
 | 
				
			||||||
 | 
							    <div class="channel-viewers  px-6 py-2 radius-pill">
 | 
				
			||||||
 | 
								{channel.viewCount + channel.viewCountRTC}
 | 
				
			||||||
 | 
							    </div>
 | 
				
			||||||
 | 
							</a>
 | 
				
			||||||
 | 
						    {/each}
 | 
				
			||||||
 | 
						</div>
 | 
				
			||||||
 | 
					    {/if}
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<style lang="scss">
 | 
					<style lang="scss">
 | 
				
			||||||
@@ -48,6 +83,15 @@
 | 
				
			|||||||
     pointer-events: var(--should-display);
 | 
					     pointer-events: var(--should-display);
 | 
				
			||||||
 }
 | 
					 }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 .channel-live {
 | 
				
			||||||
 | 
					     align-self: normal;
 | 
				
			||||||
 | 
					     font-size: 8px;
 | 
				
			||||||
 | 
					 }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 .channel-viewers {
 | 
				
			||||||
 | 
					     font-weight: 700;
 | 
				
			||||||
 | 
					 }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 .servers {
 | 
					 .servers {
 | 
				
			||||||
     width: var(--sidebar-server-select-width);
 | 
					     width: var(--sidebar-server-select-width);
 | 
				
			||||||
     background-color: var(--base-500);
 | 
					     background-color: var(--base-500);
 | 
				
			||||||
@@ -75,6 +119,29 @@
 | 
				
			|||||||
	     background-color: var(--base-700);
 | 
						     background-color: var(--base-700);
 | 
				
			||||||
	     color: var(--base-300);
 | 
						     color: var(--base-300);
 | 
				
			||||||
	 }
 | 
						 }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					     }
 | 
				
			||||||
 | 
					 }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 a {
 | 
				
			||||||
 | 
					     background-color: var(--base-400);
 | 
				
			||||||
 | 
					     color: var(--base-700);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					     transition-property: background-color, color, border-radius;
 | 
				
			||||||
 | 
					     transition-duration: 80ms;
 | 
				
			||||||
 | 
					     transition-timing-function: ease-out;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					     &:hover {
 | 
				
			||||||
 | 
						 background-color: var(--base-300);
 | 
				
			||||||
 | 
					     }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					     &.selected {
 | 
				
			||||||
 | 
						 background-color: var(--base-700);
 | 
				
			||||||
 | 
						 color: var(--base-300);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						 & .channel-viewers {
 | 
				
			||||||
 | 
						     color: var(--red);
 | 
				
			||||||
 | 
						 }
 | 
				
			||||||
     }
 | 
					     }
 | 
				
			||||||
 }
 | 
					 }
 | 
				
			||||||
</style>
 | 
					</style>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -97,10 +97,46 @@ globalMode.subscribe((v) => {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
export const shouldTweet = writable(false);
 | 
					export const shouldTweet = writable(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type User = {
 | 
				
			||||||
 | 
					  color: string;
 | 
				
			||||||
 | 
					  page: string;
 | 
				
			||||||
 | 
					  username: string;
 | 
				
			||||||
 | 
					  unum: number;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type Channel = Record<
 | 
				
			||||||
 | 
					  | 'avatar'
 | 
				
			||||||
 | 
					  | 'channel'
 | 
				
			||||||
 | 
					  | 'cover'
 | 
				
			||||||
 | 
					  | 'desc'
 | 
				
			||||||
 | 
					  | 'name'
 | 
				
			||||||
 | 
					  | 'src'
 | 
				
			||||||
 | 
					  | 'thumbnail'
 | 
				
			||||||
 | 
					  | 'title'
 | 
				
			||||||
 | 
					  | 'type'
 | 
				
			||||||
 | 
					  | 'url'
 | 
				
			||||||
 | 
					  | 'user',
 | 
				
			||||||
 | 
					  string
 | 
				
			||||||
 | 
					> &
 | 
				
			||||||
 | 
					  Record<'live' | 'nsfw', boolean> &
 | 
				
			||||||
 | 
					  Record<'viewCount' | 'viewCountRTC', number> & {
 | 
				
			||||||
 | 
					    users: Record<string, { watching: string[]; data: User }>[];
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					let setChannels: Subscriber<any> | undefined;
 | 
				
			||||||
 | 
					export const channels = readable<Channel[]>(chat.channelViewers, (setf) => {
 | 
				
			||||||
 | 
					  setChannels = setf;
 | 
				
			||||||
 | 
					  return () => {
 | 
				
			||||||
 | 
					    setChannels = undefined;
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					chat.onUpdateUsernames = (vs) => setChannels?.(vs) as any;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Essentially a shared_ptr idiom. When nobody is subscribed to this, the chat
 | 
					// Essentially a shared_ptr idiom. When nobody is subscribed to this, the chat
 | 
				
			||||||
// d/c's.
 | 
					// d/c's.
 | 
				
			||||||
export const chat_lock = readable(null, () => {
 | 
					export const chat_lock = readable(null, () => {
 | 
				
			||||||
  const release_connected = connected.subscribe(() => {});
 | 
					  const release_connected = connected.subscribe(() => {});
 | 
				
			||||||
 | 
					  const release_channels = channels.subscribe(() => {});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const creds = get(credentials);
 | 
					  const creds = get(credentials);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -111,6 +147,7 @@ export const chat_lock = readable(null, () => {
 | 
				
			|||||||
    setConnected?.(false);
 | 
					    setConnected?.(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    release_connected();
 | 
					    release_connected();
 | 
				
			||||||
 | 
					    release_channels();
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user