Initial commit 🍀
This commit is contained in:
commit
b3f42e62af
405 changed files with 31017 additions and 0 deletions
108
src/web/app/desktop/scripts/autocomplete.ls
Normal file
108
src/web/app/desktop/scripts/autocomplete.ls
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
# Autocomplete
|
||||
#================================
|
||||
|
||||
get-caret-coordinates = require 'textarea-caret-position'
|
||||
riot = require 'riot'
|
||||
|
||||
# オートコンプリートを管理するクラスです。
|
||||
class Autocomplete
|
||||
|
||||
@textarea = null
|
||||
@suggestion = null
|
||||
|
||||
# 対象のテキストエリアを与えてインスタンスを初期化します。
|
||||
(textarea) ~>
|
||||
@textarea = textarea
|
||||
|
||||
# このインスタンスにあるテキストエリアの入力のキャプチャを開始します。
|
||||
attach: ~>
|
||||
@textarea.add-event-listener \input @on-input
|
||||
|
||||
# このインスタンスにあるテキストエリアの入力のキャプチャを解除します。
|
||||
detach: ~>
|
||||
@textarea.remove-event-listener \input @on-input
|
||||
@close!
|
||||
|
||||
# テキスト入力時
|
||||
on-input: ~>
|
||||
@close!
|
||||
|
||||
caret = @textarea.selection-start
|
||||
text = @textarea.value.substr 0 caret
|
||||
|
||||
mention-index = text.last-index-of \@
|
||||
|
||||
if mention-index == -1
|
||||
return
|
||||
|
||||
username = text.substr mention-index + 1
|
||||
|
||||
if not username.match /^[a-zA-Z0-9-]+$/
|
||||
return
|
||||
|
||||
@open \user username
|
||||
|
||||
# サジェストを提示します。
|
||||
open: (type, q) ~>
|
||||
# 既に開いているサジェストは閉じる
|
||||
@close!
|
||||
|
||||
# サジェスト要素作成
|
||||
suggestion = document.create-element \mk-autocomplete-suggestion
|
||||
|
||||
# ~ サジェストを表示すべき位置を計算 ~
|
||||
|
||||
caret-position = get-caret-coordinates @textarea, @textarea.selection-start
|
||||
|
||||
rect = @textarea.get-bounding-client-rect!
|
||||
|
||||
x = rect.left + window.page-x-offset + caret-position.left
|
||||
y = rect.top + window.page-y-offset + caret-position.top
|
||||
|
||||
suggestion.style.left = x + \px
|
||||
suggestion.style.top = y + \px
|
||||
|
||||
# 要素追加
|
||||
el = document.body.append-child suggestion
|
||||
|
||||
# マウント
|
||||
mounted = riot.mount el, do
|
||||
textarea: @textarea
|
||||
complete: @complete
|
||||
close: @close
|
||||
type: type
|
||||
q: q
|
||||
|
||||
@suggestion = mounted.0
|
||||
|
||||
# サジェストを閉じます。
|
||||
close: ~>
|
||||
if !@suggestion?
|
||||
return
|
||||
|
||||
@suggestion.unmount!
|
||||
@suggestion = null
|
||||
|
||||
@textarea.focus!
|
||||
|
||||
# オートコンプリートする
|
||||
complete: (user) ~>
|
||||
@close!
|
||||
value = user.username
|
||||
|
||||
caret = @textarea.selection-start
|
||||
source = @textarea.value
|
||||
|
||||
before = source.substr 0 caret
|
||||
trimed-before = before.substring 0 before.last-index-of \@
|
||||
after = source.substr caret
|
||||
|
||||
# 結果を挿入する
|
||||
@textarea.value = trimed-before + \@ + value + ' ' + after
|
||||
|
||||
# キャレットを戻す
|
||||
@textarea.focus!
|
||||
pos = caret + value.length
|
||||
@textarea.set-selection-range pos, pos
|
||||
|
||||
module.exports = Autocomplete
|
||||
17
src/web/app/desktop/scripts/dialog.ls
Normal file
17
src/web/app/desktop/scripts/dialog.ls
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
# Dialog
|
||||
#================================
|
||||
|
||||
riot = require 'riot'
|
||||
|
||||
module.exports = (title, text, buttons, can-through, on-through) ~>
|
||||
dialog = document.body.append-child document.create-element \mk-dialog
|
||||
controller = riot.observable!
|
||||
riot.mount dialog, do
|
||||
controller: controller
|
||||
title: title
|
||||
text: text
|
||||
buttons: buttons
|
||||
can-through: can-through
|
||||
on-through: on-through
|
||||
controller.trigger \open
|
||||
return controller
|
||||
56
src/web/app/desktop/scripts/follow-scroll.ls
Normal file
56
src/web/app/desktop/scripts/follow-scroll.ls
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
class Follower
|
||||
(el) ->
|
||||
@follower = el
|
||||
@last-scroll-top = window.scroll-y
|
||||
@initial-follower-top = @follower.get-bounding-client-rect!.top
|
||||
@page-top = 48
|
||||
|
||||
follow: ->
|
||||
window-height = window.inner-height
|
||||
follower-height = @follower.offset-height
|
||||
|
||||
scroll-top = window.scroll-y
|
||||
scroll-bottom = scroll-top + window-height
|
||||
|
||||
follower-top = @follower.get-bounding-client-rect!.top + scroll-top
|
||||
follower-bottom = follower-top + follower-height
|
||||
|
||||
height-delta = Math.abs window-height - follower-height
|
||||
scroll-delta = @last-scroll-top - scroll-top
|
||||
|
||||
is-scrolling-down = (scroll-top > @last-scroll-top)
|
||||
is-window-larger = (window-height > follower-height)
|
||||
|
||||
console.log @initial-follower-top
|
||||
|
||||
if (is-window-larger && scroll-top > @initial-follower-top) || (!is-window-larger && scroll-top > @initial-follower-top + height-delta)
|
||||
@follower.class-list.add \fixed
|
||||
else if !is-scrolling-down && scroll-top + @page-top <= @initial-follower-top
|
||||
@follower.class-list.remove \fixed
|
||||
@follower.style.top = 0
|
||||
return
|
||||
|
||||
drag-bottom-down = (follower-bottom <= scroll-bottom && is-scrolling-down)
|
||||
drag-top-up = (follower-top >= scroll-top + @page-top && !is-scrolling-down)
|
||||
|
||||
if drag-bottom-down
|
||||
console.log \down
|
||||
@follower.style.top = if is-window-larger then 0 else -height-delta + \px
|
||||
else if drag-top-up
|
||||
console.log \up
|
||||
@follower.style.top = @page-top + \px
|
||||
else if @follower.class-list.contains \fixed
|
||||
console.log \-
|
||||
current-top = parse-int @follower.style.top, 10
|
||||
|
||||
min-top = -height-delta
|
||||
scrolled-top = current-top + scroll-delta
|
||||
|
||||
is-page-at-bottom = (scroll-top + window-height >= document.body.offset-height)
|
||||
new-top = if is-page-at-bottom then min-top else scrolled-top
|
||||
|
||||
@follower.style.top = new-top + \px
|
||||
|
||||
@last-scroll-top = scroll-top
|
||||
|
||||
module.exports = Follower
|
||||
19
src/web/app/desktop/scripts/fuck-ad-block.ls
Normal file
19
src/web/app/desktop/scripts/fuck-ad-block.ls
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
# FUCK AD BLOCK
|
||||
#================================
|
||||
|
||||
require 'fuck-adblock'
|
||||
dialog = require './dialog.ls'
|
||||
|
||||
module.exports = ~>
|
||||
if fuck-ad-block == undefined
|
||||
ad-block-detected!
|
||||
else
|
||||
fuck-ad-block.on-detected ad-block-detected
|
||||
|
||||
function ad-block-detected
|
||||
dialog do
|
||||
'<i class="fa fa-exclamation-triangle"></i>広告ブロッカーを無効にしてください'
|
||||
'<strong>Misskeyは広告を掲載していません</strong>が、広告をブロックする機能が有効だと一部の機能が利用できなかったり、不具合が発生する場合があります。'
|
||||
[
|
||||
text: \OK
|
||||
]
|
||||
13
src/web/app/desktop/scripts/input-dialog.ls
Normal file
13
src/web/app/desktop/scripts/input-dialog.ls
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
# Input Dialog
|
||||
#================================
|
||||
|
||||
riot = require 'riot'
|
||||
|
||||
module.exports = (title, placeholder, default-value, on-ok, on-cancel) ~>
|
||||
dialog = document.body.append-child document.create-element \mk-input-dialog
|
||||
riot.mount dialog, do
|
||||
title: title
|
||||
placeholder: placeholder
|
||||
default: default-value
|
||||
on-ok: on-ok
|
||||
on-cancel: on-cancel
|
||||
6
src/web/app/desktop/scripts/notify.ls
Normal file
6
src/web/app/desktop/scripts/notify.ls
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
riot = require \riot
|
||||
|
||||
module.exports = (message) ~>
|
||||
notification = document.body.append-child document.create-element \mk-ui-notification
|
||||
riot.mount notification, do
|
||||
message: message
|
||||
8
src/web/app/desktop/scripts/open-window.ls
Normal file
8
src/web/app/desktop/scripts/open-window.ls
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
riot = require \riot
|
||||
|
||||
function open(name, opts)
|
||||
window = document.body.append-child document.create-element name
|
||||
riot.mount window, opts
|
||||
|
||||
riot.mixin \open-window do
|
||||
open-window: open
|
||||
38
src/web/app/desktop/scripts/stream.ls
Normal file
38
src/web/app/desktop/scripts/stream.ls
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
# Stream
|
||||
#================================
|
||||
|
||||
stream = require '../../common/scripts/stream.ls'
|
||||
get-post-summary = require '../../common/scripts/get-post-summary.ls'
|
||||
riot = require \riot
|
||||
|
||||
module.exports = (me) ~>
|
||||
s = stream me
|
||||
|
||||
s.event.on \drive_file_created (file) ~>
|
||||
n = new Notification 'ファイルがアップロードされました' do
|
||||
body: file.name
|
||||
icon: file.url + '?thumbnail&size=64'
|
||||
set-timeout (n.close.bind n), 5000ms
|
||||
|
||||
s.event.on \mention (post) ~>
|
||||
n = new Notification "#{post.user.name}さんから:" do
|
||||
body: get-post-summary post
|
||||
icon: post.user.avatar_url + '?thumbnail&size=64'
|
||||
set-timeout (n.close.bind n), 6000ms
|
||||
|
||||
s.event.on \reply (post) ~>
|
||||
n = new Notification "#{post.user.name}さんから返信:" do
|
||||
body: get-post-summary post
|
||||
icon: post.user.avatar_url + '?thumbnail&size=64'
|
||||
set-timeout (n.close.bind n), 6000ms
|
||||
|
||||
s.event.on \quote (post) ~>
|
||||
n = new Notification "#{post.user.name}さんが引用:" do
|
||||
body: get-post-summary post
|
||||
icon: post.user.avatar_url + '?thumbnail&size=64'
|
||||
set-timeout (n.close.bind n), 6000ms
|
||||
|
||||
riot.mixin \stream do
|
||||
stream: s.event
|
||||
get-stream-state: s.get-state
|
||||
stream-state-ev: s.state-ev
|
||||
81
src/web/app/desktop/scripts/update-avatar.ls
Normal file
81
src/web/app/desktop/scripts/update-avatar.ls
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
# Update Avatar
|
||||
#================================
|
||||
|
||||
riot = require 'riot'
|
||||
dialog = require './dialog.ls'
|
||||
api = require '../../common/scripts/api.ls'
|
||||
|
||||
module.exports = (I, cb, file = null) ~>
|
||||
|
||||
@file-selected = (file) ~>
|
||||
cropper = document.body.append-child document.create-element \mk-crop-window
|
||||
cropper = riot.mount cropper, do
|
||||
file: file
|
||||
title: 'アバターとして表示する部分を選択'
|
||||
aspect-ratio: 1 / 1
|
||||
.0
|
||||
cropper.on \cropped (blob) ~>
|
||||
data = new FormData!
|
||||
data.append \i I.token
|
||||
data.append \file blob, file.name + '.cropped.png'
|
||||
api I, \drive/folders/find do
|
||||
name: 'アイコン'
|
||||
.then (icon-folder) ~>
|
||||
if icon-folder.length == 0
|
||||
api I, \drive/folders/create do
|
||||
name: 'アイコン'
|
||||
.then (icon-folder) ~>
|
||||
@uplaod data, icon-folder
|
||||
else
|
||||
@uplaod data, icon-folder.0
|
||||
cropper.on \skiped ~>
|
||||
@set file
|
||||
|
||||
@uplaod = (data, folder) ~>
|
||||
|
||||
progress = document.body.append-child document.create-element \mk-progress-dialog
|
||||
progress = riot.mount progress, do
|
||||
title: '新しいアバターをアップロードしています'
|
||||
.0
|
||||
|
||||
if folder?
|
||||
data.append \folder_id folder.id
|
||||
|
||||
xhr = new XMLHttpRequest!
|
||||
xhr.open \POST CONFIG.api.url + \/drive/files/create true
|
||||
xhr.onload = (e) ~>
|
||||
file = JSON.parse e.target.response
|
||||
progress.close!
|
||||
@set file
|
||||
|
||||
xhr.upload.onprogress = (e) ~>
|
||||
if e.length-computable
|
||||
progress.update-progress e.loaded, e.total
|
||||
|
||||
xhr.send data
|
||||
|
||||
@set = (file) ~>
|
||||
api I, \i/update do
|
||||
avatar_id: file.id
|
||||
.then (i) ~>
|
||||
dialog do
|
||||
'<i class="fa fa-info-circle"></i>アバターを更新しました'
|
||||
'新しいアバターが反映されるまで時間がかかる場合があります。'
|
||||
[
|
||||
text: \わかった
|
||||
]
|
||||
if cb? then cb i
|
||||
.catch (err) ~>
|
||||
console.error err
|
||||
#@opts.ui.trigger \notification 'Error!'
|
||||
|
||||
if file?
|
||||
@file-selected file
|
||||
else
|
||||
browser = document.body.append-child document.create-element \mk-select-file-from-drive-window
|
||||
browser = riot.mount browser, do
|
||||
multiple: false
|
||||
title: '<i class="fa fa-picture-o"></i>アバターにする画像を選択'
|
||||
.0
|
||||
browser.one \selected (file) ~>
|
||||
@file-selected file
|
||||
81
src/web/app/desktop/scripts/update-banner.ls
Normal file
81
src/web/app/desktop/scripts/update-banner.ls
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
# Update Banner
|
||||
#================================
|
||||
|
||||
riot = require 'riot'
|
||||
dialog = require './dialog.ls'
|
||||
api = require '../../common/scripts/api.ls'
|
||||
|
||||
module.exports = (I, cb, file = null) ~>
|
||||
|
||||
@file-selected = (file) ~>
|
||||
cropper = document.body.append-child document.create-element \mk-crop-window
|
||||
cropper = riot.mount cropper, do
|
||||
file: file
|
||||
title: 'バナーとして表示する部分を選択'
|
||||
aspect-ratio: 16 / 9
|
||||
.0
|
||||
cropper.on \cropped (blob) ~>
|
||||
data = new FormData!
|
||||
data.append \i I.token
|
||||
data.append \file blob, file.name + '.cropped.png'
|
||||
api I, \drive/folders/find do
|
||||
name: 'バナー'
|
||||
.then (banner-folder) ~>
|
||||
if banner-folder.length == 0
|
||||
api I, \drive/folders/create do
|
||||
name: 'バナー'
|
||||
.then (banner-folder) ~>
|
||||
@uplaod data, banner-folder
|
||||
else
|
||||
@uplaod data, banner-folder.0
|
||||
cropper.on \skiped ~>
|
||||
@set file
|
||||
|
||||
@uplaod = (data, folder) ~>
|
||||
|
||||
progress = document.body.append-child document.create-element \mk-progress-dialog
|
||||
progress = riot.mount progress, do
|
||||
title: '新しいバナーをアップロードしています'
|
||||
.0
|
||||
|
||||
if folder?
|
||||
data.append \folder_id folder.id
|
||||
|
||||
xhr = new XMLHttpRequest!
|
||||
xhr.open \POST CONFIG.api.url + \/drive/files/create true
|
||||
xhr.onload = (e) ~>
|
||||
file = JSON.parse e.target.response
|
||||
progress.close!
|
||||
@set file
|
||||
|
||||
xhr.upload.onprogress = (e) ~>
|
||||
if e.length-computable
|
||||
progress.update-progress e.loaded, e.total
|
||||
|
||||
xhr.send data
|
||||
|
||||
@set = (file) ~>
|
||||
api I, \i/update do
|
||||
banner_id: file.id
|
||||
.then (i) ~>
|
||||
dialog do
|
||||
'<i class="fa fa-info-circle"></i>バナーを更新しました'
|
||||
'新しいバナーが反映されるまで時間がかかる場合があります。'
|
||||
[
|
||||
text: \わかりました。
|
||||
]
|
||||
if cb? then cb i
|
||||
.catch (err) ~>
|
||||
console.error err
|
||||
#@opts.ui.trigger \notification 'Error!'
|
||||
|
||||
if file?
|
||||
@file-selected file
|
||||
else
|
||||
browser = document.body.append-child document.create-element \mk-select-file-from-drive-window
|
||||
browser = riot.mount browser, do
|
||||
multiple: false
|
||||
title: '<i class="fa fa-picture-o"></i>バナーにする画像を選択'
|
||||
.0
|
||||
browser.one \selected (file) ~>
|
||||
@file-selected file
|
||||
35
src/web/app/desktop/scripts/update-wallpaper.ls
Normal file
35
src/web/app/desktop/scripts/update-wallpaper.ls
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
# Update Wallpaper
|
||||
#================================
|
||||
|
||||
riot = require 'riot'
|
||||
dialog = require './dialog.ls'
|
||||
api = require '../../common/scripts/api.ls'
|
||||
|
||||
module.exports = (I, cb, file = null) ~>
|
||||
|
||||
@set = (file) ~>
|
||||
api I, \i/appdata/set do
|
||||
data: JSON.stringify do
|
||||
wallpaper: file.id
|
||||
.then (i) ~>
|
||||
dialog do
|
||||
'<i class="fa fa-info-circle"></i>壁紙を更新しました'
|
||||
'新しい壁紙が反映されるまで時間がかかる場合があります。'
|
||||
[
|
||||
text: \はい
|
||||
]
|
||||
if cb? then cb i
|
||||
.catch (err) ~>
|
||||
console.error err
|
||||
#@opts.ui.trigger \notification 'Error!'
|
||||
|
||||
if file?
|
||||
@set file
|
||||
else
|
||||
browser = document.body.append-child document.create-element \mk-select-file-from-drive-window
|
||||
browser = riot.mount browser, do
|
||||
multiple: false
|
||||
title: '<i class="fa fa-picture-o"></i>壁紙にする画像を選択'
|
||||
.0
|
||||
browser.one \selected (file) ~>
|
||||
@set file
|
||||
74
src/web/app/desktop/scripts/user-preview.ls
Normal file
74
src/web/app/desktop/scripts/user-preview.ls
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
# User Preview
|
||||
#================================
|
||||
|
||||
riot = require \riot
|
||||
|
||||
riot.mixin \user-preview do
|
||||
init: ->
|
||||
@on \mount ~>
|
||||
scan.call @
|
||||
@on \updated ~>
|
||||
scan.call @
|
||||
|
||||
function scan
|
||||
elems = @root.query-selector-all '[data-user-preview]:not([data-user-preview-attached])'
|
||||
elems.for-each attach.bind @
|
||||
|
||||
function attach el
|
||||
el.set-attribute \data-user-preview-attached true
|
||||
user = el.get-attribute \data-user-preview
|
||||
|
||||
tag = null
|
||||
|
||||
show-timer = null
|
||||
hide-timer = null
|
||||
|
||||
el.add-event-listener \mouseover ~>
|
||||
clear-timeout show-timer
|
||||
clear-timeout hide-timer
|
||||
show-timer := set-timeout ~>
|
||||
show!
|
||||
, 500ms
|
||||
|
||||
el.add-event-listener \mouseleave ~>
|
||||
clear-timeout show-timer
|
||||
clear-timeout hide-timer
|
||||
hide-timer := set-timeout ~>
|
||||
close!
|
||||
, 500ms
|
||||
|
||||
@on \unmount ~>
|
||||
clear-timeout show-timer
|
||||
clear-timeout hide-timer
|
||||
close!
|
||||
|
||||
function show
|
||||
if tag?
|
||||
return
|
||||
|
||||
preview = document.create-element \mk-user-preview
|
||||
|
||||
rect = el.get-bounding-client-rect!
|
||||
x = rect.left + el.offset-width + window.page-x-offset
|
||||
y = rect.top + window.page-y-offset
|
||||
|
||||
preview.style.top = y + \px
|
||||
preview.style.left = x + \px
|
||||
|
||||
preview.add-event-listener \mouseover ~>
|
||||
clear-timeout hide-timer
|
||||
|
||||
preview.add-event-listener \mouseleave ~>
|
||||
clear-timeout show-timer
|
||||
hide-timer := set-timeout ~>
|
||||
close!
|
||||
, 500ms
|
||||
|
||||
tag := riot.mount (document.body.append-child preview), do
|
||||
user: user
|
||||
.0
|
||||
|
||||
function close
|
||||
if tag?
|
||||
tag.close!
|
||||
tag := null
|
||||
Loading…
Add table
Add a link
Reference in a new issue