| @@ -7,15 +7,6 @@ | |||
| right: 0; | |||
| top: 0; | |||
| } | |||
| .not-chat { | |||
| z-index: 999; | |||
| background-color: rgba(0, 0, 0, 0.8); | |||
| position: fixed; | |||
| height: 100vh; | |||
| width: 100vw; | |||
| right: 0; | |||
| top: 0; | |||
| } | |||
| .chat .actions { | |||
| display: grid; | |||
| grid-template-columns: 1fr auto; | |||
| @@ -60,6 +51,6 @@ | |||
| margin: 3px 3px; | |||
| line-height: 1.5; | |||
| } | |||
| .badge.on { | |||
| .badge.hot { | |||
| background-color: crimson; | |||
| } | |||
| @@ -100,14 +100,11 @@ const Post = { | |||
| } | |||
| } | |||
| const ChatConfig = { | |||
| isOn: false, | |||
| toggle() { | |||
| ChatConfig.isOn = !ChatConfig.isOn | |||
| }, | |||
| view() { | |||
| const on = Chat.unseenCount ? '.on' : '' | |||
| return m('button', {onclick: this.toggle}, 'chat ', | |||
| m('.badge' + on, Chat.unseenCount), | |||
| const onclick = () => {Chat.isOn = !Chat.isOn} | |||
| const hot = Chat.unseenCount ? '.hot' : '' | |||
| return m('button', {onclick}, 'chat ', | |||
| m('.badge' + hot, Chat.unseenCount), | |||
| ) | |||
| }, | |||
| } | |||
| @@ -116,20 +113,18 @@ const Chat = { | |||
| unseenCount: 0, | |||
| originalTitle: 'pico.chat', | |||
| onupdate: () => { | |||
| if(document.hasFocus() && ChatConfig.isOn) { | |||
| if(document.hasFocus() && Chat.isOn) { | |||
| Chat.unseenCount = 0 | |||
| textbox.focus() | |||
| } | |||
| const extra = Chat.unseenCount ? ` (${Chat.unseenCount})` : `` | |||
| document.title = Chat.originalTitle + extra | |||
| }, | |||
| view() { | |||
| return ChatConfig.isOn ? [ | |||
| m('.not-chat', {onclick: () => ChatConfig.isOn = false}), | |||
| m('.chat', | |||
| m('.posts', Chat.posts.map(post => m(Post, {post}))), | |||
| m(TextBox), | |||
| ) | |||
| ] : null | |||
| return m('.chat', | |||
| m('.posts', Chat.posts.map(post => m(Post, {post}))), | |||
| m(TextBox), | |||
| ) | |||
| }, | |||
| } | |||
| addEventListener('focus', m.redraw) | |||
| @@ -1,6 +1,10 @@ | |||
| const VolumeMap = { | |||
| isOn: false, | |||
| size: 200, | |||
| positions: new Map(), | |||
| toggle() { | |||
| VolumeMap.isOn = !VolumeMap.isOn | |||
| }, | |||
| onremove() { | |||
| VolumeMap.positions.clear() | |||
| }, | |||
| @@ -88,6 +88,31 @@ const connect = (username) => { | |||
| * BASE | |||
| * | |||
| */ | |||
| const Shadow = { | |||
| oncreate({dom, attrs}) { | |||
| dom.listener = () => attrs.app.isOn = !attrs.app.isOn | |||
| addEventListener(attrs.key, dom.listener) | |||
| }, | |||
| onremove({dom, attrs}) { | |||
| removeEventListener(attrs.key, dom.listener) | |||
| }, | |||
| view({attrs}) { | |||
| const style = { | |||
| zIndex: 999, | |||
| backgroundColor: 'rgba(0, 0, 0, 0.8)', | |||
| visibility: attrs.app.isOn ? 'unset' : 'hidden', | |||
| position: 'fixed', | |||
| height: '100vh', | |||
| width: '100vw', | |||
| right: 0, | |||
| top: 0, | |||
| } | |||
| const onclick = ({target: {classList}}) => { | |||
| classList.contains('shadow') && signal({kind: attrs.key}) | |||
| } | |||
| return m('.shadow', {style, onclick}, m(attrs.app)) | |||
| }, | |||
| } | |||
| const Settings = { | |||
| get(key) { | |||
| try { | |||
| @@ -167,6 +192,7 @@ const Base = { | |||
| m('button', {onclick: Base.sendLogout}, 'settings'), | |||
| m(VideoConfig), | |||
| m(ChatConfig), | |||
| m('button', {onclick: VolumeMap.toggle}, 'volume'), | |||
| ] : [ | |||
| m('form.login', | |||
| {onsubmit: Base.sendLogin}, | |||
| @@ -177,9 +203,9 @@ const Base = { | |||
| m('span.error', State.info), | |||
| ), | |||
| State.isConnected ? [ | |||
| m(StreamContainer), | |||
| m(Chat), | |||
| params.get('map') === 'true' ? m(VolumeMap) : null, | |||
| m(StreamContainer, {key: 'lolo'}), | |||
| m(Shadow, {key: 'chat-shadow', app: Chat}), | |||
| m(Shadow, {key: 'map-shadow', app: VolumeMap}), | |||
| ] : m(Settings), | |||
| ) | |||
| }, | |||