From 2827f852c088f2036f8eb85dfc87665b8e980c3b Mon Sep 17 00:00:00 2001 From: Andrew Date: Thu, 26 Oct 2017 23:19:20 -0700 Subject: Theme: Windows 95 --- app/javascript/styles/win95.scss | 1457 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 1457 insertions(+) create mode 100644 app/javascript/styles/win95.scss (limited to 'app/javascript/styles') diff --git a/app/javascript/styles/win95.scss b/app/javascript/styles/win95.scss new file mode 100644 index 000000000..994fab4bf --- /dev/null +++ b/app/javascript/styles/win95.scss @@ -0,0 +1,1457 @@ +$win95-bg: #bfbfbf; +$win95-window-header: #00007f; +$win95-tooltip-yellow: #ffffcc; + +@mixin win95-border-outset() { + border-left: 2px solid #efefef; + border-top: 2px solid #efefef; + border-right: 2px solid #404040; + border-bottom: 2px solid #404040; + border-radius:0px; +} + +@mixin win95-outset() { + box-shadow: inset -1px -1px 0px #000000, + inset 1px 1px 0px #ffffff, + inset -2px -2px 0px #808080, + inset 2px 2px 0px #dfdfdf; + border-radius:0px; +} + +@mixin win95-border-inset() { + border-left: 2px solid #404040; + border-top: 2px solid #404040; + border-right: 2px solid #efefef; + border-bottom: 2px solid #efefef; + border-radius:0px; +} + +@mixin win95-border-slight-inset() { + border-left: 1px solid #404040; + border-top: 1px solid #404040; + border-right: 1px solid #efefef; + border-bottom: 1px solid #efefef; + border-radius:0px; +} + +@mixin win95-inset() { + box-shadow: inset 1px 1px 0px #000000, + inset -1px -1px 0px #ffffff, + inset 2px 2px 0px #808080, + inset -2px -2px 0px #dfdfdf; + border-width:0px; + border-radius:0px; +} + + +@mixin win95-tab() { + box-shadow: inset -1px 0px 0px #000000, + inset 1px 0px 0px #ffffff, + inset 0px 1px 0px #ffffff, + inset 0px 2px 0px #dfdfdf, + inset -2px 0px 0px #808080, + inset 2px 0px 0px #dfdfdf; + border-radius:0px; + border-top-left-radius: 1px; + border-top-right-radius: 1px; +} + +@mixin win95-reset() { + box-shadow: unset; +} + +@font-face { + font-family:"premillenium"; + src: url('../fonts/premillenium/MSSansSerif.ttf') format('truetype'); +} + +@import 'application'; + +body { + font-size:13px; + font-family: "MS Sans Serif", "premillenium", sans-serif; + color:black; +} + +.ui, +.ui .columns-area, +body.admin { + background: #008080; +} + +.loading-bar { + height:5px; + background-color: #000080; +} + +.tabs-bar { + background: $win95-bg; + @include win95-outset() + height: 30px; +} + +.tabs-bar__link { + color:black; + border:2px outset $win95-bg; + border-top-width: 1px; + border-left-width: 1px; + margin:2px; + padding:3px; +} + +.tabs-bar__link.active { + @include win95-inset(); + color:black; +} + +.tabs-bar__link:last-child::before { + content:"Start"; + color:black; + font-weight:bold; + font-size:15px; + width:80%; + display:block; + position:absolute; + right:0px; +} + +.tabs-bar__link:last-child { + position:relative; + flex-basis:60px !important; + font-size:0px; + color:$win95-bg; + + background-image: url("../images/start.png"); + background-repeat:no-repeat; + background-position:8%; + background-clip:padding-box; + background-size:auto 50%; +} + +.drawer .drawer__inner { + overflow: visible; + height:inherit; +} + +.drawer__pager { + overflow-y:auto; +} + +.column { + max-height:100vh; +} + +.column > .scrollable { + background: $win95-bg; + @include win95-border-outset() + border-top-width:0px; +} + +.column-header__wrapper { + color:white; + font-weight:bold; + background:#7f7f7f; +} + +.column-header { + padding:2px; + font-size:13px; + background:#7f7f7f; + @include win95-border-outset() + border-bottom-width:0px; + color:white; + font-weight:bold; +} + +.column-header__wrapper.active { + background:$win95-window-header; +} + +.column-header__wrapper.active::before { + display:none; +} +.column-header.active { + box-shadow:unset; + background:$win95-window-header; +} + +.column-header.active .column-header__icon { + color:white; +} + +.column-header__button { + background: $win95-bg; + color: black; + line-height:0px; + font-size:14px; + max-height:20px; + padding:0px 2px; + margin-top:2px; + @include win95-outset() +} + +.column-header__button.active, .column-header__button.active:hover { + @include win95-inset(); + background-color:#7f7f7f; +} + +.column-header__back-button { + background: $win95-bg; + color: black; + padding:2px; + max-height:20px; + margin-top:2px; + @include win95-outset() + font-size:13px; + font-weight:bold; +} + +.column-back-button { + background:$win95-bg; + color:black; + @include win95-outset() + padding:2px; + font-size:13px; + font-weight:bold; +} + +.column-back-button--slim-button { + position:absolute; + top:-22px; + right:4px; + max-height:20px; + max-width:60px; + padding:0px 2px; +} + +.column-back-button__icon { + font-size:11px; + margin-top:-3px; +} + +.column-header__collapsible { + border-left:2px outset $win95-bg; + border-right:2px outset $win95-bg; +} + +.column-header__collapsible-inner { + background:$win95-bg; + color:black; +} + +.column-header__collapsible__extra { + color:black; +} + +.column-header__collapsible__extra div[role="group"] { + border: 2px groove $win95-bg; + border-radius:4px; + margin-bottom:8px; + padding:4px; +} + +.column-settings__section { + color:black; + font-weight:bold; + font-size:11px; + position:relative; + top: -12px; + left:4px; + background-color:$win95-bg; + display:inline-block; + padding:0px 4px; + margin-bottom:0px; +} + +.setting-meta__label, .setting-toggle__label { + color:black; + font-weight:normal; +} + +.setting-meta__label span:before { + content:"("; +} +.setting-meta__label span:after { + content:")"; +} + +.setting-toggle { + line-height:13px; +} + +.react-toggle .react-toggle-track { + border-radius:0px; + background-color:white; + @include win95-border-inset(); + + width:12px; + height:12px; +} + +.react-toggle:hover:not(.react-toggle--disabled) .react-toggle-track { + background-color:white; +} + +.react-toggle .react-toggle-track-check { + left:2px; + transition:unset; +} + +.react-toggle .react-toggle-track-check svg path { + fill: black; +} + +.react-toggle .react-toggle-track-x { + display:none; +} + +.react-toggle .react-toggle-thumb { + border-radius:0px; + display:none; +} + +.text-btn { + background-color:$win95-bg; + @include win95-outset() + padding:4px; +} + +.text-btn:hover { + text-decoration:none; + color:black; +} + +.text-btn:active { + @include win95-inset(); +} + +.setting-text { + color:black; + background-color:white; + @include win95-inset(); + font-size:13px; + padding:2px; +} + +.setting-text:active, .setting-text:focus, +.setting-text.light:active, .setting-text.light:focus { + color:black; + border-bottom:2px inset $win95-bg; +} + +.column-header__setting-arrows .column-header__setting-btn { + padding:3px 10px; +} + +.column-header__setting-arrows .column-header__setting-btn:last-child { + padding:3px 10px; +} + +.missing-indicator { + background-color:$win95-bg; + color:black; + @include win95-outset() +} + +.missing-indicator > div { + background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAARCAYAAAA7bUf6AAAACXBIWXMAAC4jAAAuIwF4pT92AAAAF3pUWHRUaXRsZQAACJnLyy9Jyy/NSwEAD5IDblIFOhoAAAAXelRYdEF1dGhvcgAACJlLzijKz0vMAQALmgLoDsFj8gAAAQpJREFUOMuVlD0OwjAMhd2oQl04Axfo0IGBgYELcAY6cqQuSO0ZOEAZGBg6VKg74gwsEaoESRVHjusI8aQqzY8/PbtOEz1qkFSn2YevlaNOpLMJh2DwvixhuXtOa6/LCh51DUMEFkAsgAZD207Doin8mQ562JpRE5CHBAAhmIqD1L8AqzUUUJkxc6kr3AgAJ+NuvIWRdk7WcrKl0AUqcIBBHOiEbpS4m27mIL5Onfg3k0rgggeQuS2sDOGSahKR+glgqaGLgUJs951NN1q9D72cQqQWR9cr3sm9YcEssEuz6eEuZh2bu0aSOhQ1MBezu2O/+TVSvEFII3qLsZWrSA2AAUQIh1HpyP/kC++zjVSMj6ntAAAAAElFTkSuQmCC') + no-repeat; + background-position:center center; +} + +.empty-column-indicator, +.error-column { + background: $win95-bg; + color: black; +} + +.status__wrapper { + border: 2px groove $win95-bg; + margin:4px; +} + +.status { + @include win95-border-slight-inset(); + background-color:white; + margin:4px; + padding-bottom:40px; + margin-bottom:8px; +} + +.status.status-direct { + background-color:$win95-bg; +} + +.status__content { + font-size:13px; +} + +.status.light .status__relative-time, +.status.light .display-name span { + color: #7f7f7f; +} + +.status__action-bar { + box-sizing:border-box; + position:absolute; + bottom:-1px; + left:-1px; + background:$win95-bg; + width:calc(100% + 2px); + padding-left:10px; + padding: 4px 2px; + padding-bottom:4px; + border-bottom:2px groove $win95-bg; + border-top:1px outset $win95-bg; + text-align: right; +} + +.status__wrapper .status__action-bar { + border-bottom-width:0px; +} + +.status__action-bar-button { + float:right; +} + +.status__action-bar-dropdown { + margin-left:auto; + margin-right:10px; +} +.status.light .status__content a { + color:blue; +} + +.dropdown__trigger.icon-button { + padding-right:6px; +} + +.detailed-status { + background:white; + background-clip:padding-box; + margin:4px; + border: 2px groove $win95-bg; + padding:4px; +} + +.detailed-status__display-name { + color:#7f7f7f; +} + +.detailed-status__display-name strong { + color:black; + font-weight:bold; +} +.account__avatar, +.account__avatar-overlay-base, +.account__header__avatar, +.account__avatar-overlay-overlay { + @include win95-border-slight-inset(); + clip-path:none; + filter: saturate(1.8) brightness(1.1); +} + +.detailed-status__action-bar { + background-color:$win95-bg; + border:0px; + border-bottom:2px groove $win95-bg; + margin-bottom:8px; + justify-items:left; + padding-left:4px; +} +.icon-button { + background:$win95-bg; + @include win95-border-outset() + padding:0px 0px 0px 0px; + margin-right:4px; +} +.icon-button, +.icon-button.inverted, +.icon-button:hover, +.icon-button.inverted:hover { + color:#3f3f3f; +} + +.icon-button:active { + @include win95-border-inset(); +} + +.status__action-bar > .icon-button { + padding:0px 15px 0px 0px; + min-width:25px; +} + +.icon-button.star-icon, +.icon-button.star-icon:active { + background:transparent; + border:none; +} + +.icon-button.star-icon > i { + background:$win95-bg; + @include win95-border-outset() + padding-bottom:3px; +} + +.icon-button.star-icon:active > i { + @include win95-border-inset(); +} + +.detailed-status__action-bar-dropdown { + margin-left:auto; + justify-content:right; + padding-right:16px; +} + +.detailed-status__button { + flex:0 0 auto; +} + +.detailed-status__button .icon-button { + padding-left:2px; + padding-right:25px; +} + +.status-card { + border-radius:0px; + background:white; + border: 1px solid black; + color:black; +} + +.status-card:hover { + background-color:white; +} + +.status-card__title { + color:blue; + text-decoration:underline; + font-weight:bold; +} + +.load-more { + width:auto; + margin:5px auto; + background: $win95-bg; + @include win95-outset(); + color:black; + padding: 2px 5px; + + &:hover { + background: $win95-bg; + color:black; + } +} + +.status-card__description { + color:black; +} + +.account__display-name strong, .status__display-name strong { + color:black; + font-weight:bold; +} + +.account .account__display-name { + color:black; +} + +.account { + border-bottom: 2px groove $win95-bg; +} + +.reply-indicator__content .status__content__spoiler-link, .status__content .status__content__spoiler-link { + background:$win95-bg; + @include win95-outset() +} + +.reply-indicator__content .status__content__spoiler-link:hover, .status__content .status__content__spoiler-link:hover { + background:$win95-bg; +} + +.reply-indicator__content .status__content__spoiler-link:active, .status__content .status__content__spoiler-link:active { + @include win95-inset(); +} + +.reply-indicator__content a, .status__content a { + color:blue; +} + +.notification { + border: 2px groove $win95-bg; + margin:4px; +} + +.notification__message { + color:black; + font-size:13px; +} + +.notification__display-name { + font-weight:bold; +} + + +.drawer__header { + background: $win95-bg; + @include win95-border-outset() + justify-content:left; + margin-bottom:0px; + padding-bottom:2px; + border-bottom:2px groove $win95-bg; +} + +.drawer__tab { + color:black; + @include win95-outset() + padding:5px; + margin:2px; + flex: 0 0 auto; +} + +.drawer__tab:first-child::before { + content:"Start"; + color:black; + font-weight:bold; + font-size:15px; + width:80%; + display:block; + position:absolute; + right:0px; + +} + +.drawer__tab:first-child { + position:relative; + padding:5px 15px; + width:40px; + font-size:0px; + color:$win95-bg; + + background-image: url("../images/start.png"); + background-repeat:no-repeat; + background-position:8%; + background-clip:padding-box; + background-size:auto 50%; +} + +.drawer__header a:hover { + background-color:transparent; +} + +.drawer__header a:first-child:hover { + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAOCAIAAACpTQvdAAAACXBIWXMAAC4jAAAuIwF4pT92AAAAF3pUWHRBdXRob3IAAAiZS84oys9LzAEAC5oC6A7BY/IAAACWSURBVCiRhVJJDsQgDEuqOfRZ7a1P5gbP4uaJaEjTADMWQhHYjlk4p0wLnNdptdF4KvBUDyGzVwc2xO+uKtH+1o0ytEEmqFpuxlvFCGCxKbNIT56QCi2MzaA/2Mz+mERSOeqzJG2RUxkjdTabgPtFoZ1bZxcKvgPcLZVufAyR9Ni8v5dWDzfFx0giC1RvZFv6l35QQ/Mvv39XXgGzQpoAAAAASUVORK5CYII="); + background-repeat:no-repeat; + background-position:8%; + background-clip:padding-box; + background-size:auto 50%; + transition:unset; +} + +.drawer__tab:first-child { + +} + +.search { + background:$win95-bg; + padding-top:2px; + padding:2px; + border:2px outset $win95-bg; + border-top-width:0px; + border-bottom: 2px groove $win95-bg; + margin-bottom:0px; +} + +.search input { + background-color:white; + color:black; + @include win95-border-slight-inset(); +} + +.search__input:focus { + background-color:white; +} + +.search-results__header { + background-color: $win95-bg; + color:black; + border-bottom:2px groove $win95-bg; +} + +.search-results__hashtag { + color:blue; +} + +.search-results__section .account:hover, +.search-results__section .account:hover .account__display-name, +.search-results__section .account:hover .account__display-name strong, +.search-results__section .search-results__hashtag:hover { + background-color:$win95-window-header; + color:white; +} + +.drawer__inner, +.drawer__inner.darker { + background-color:$win95-bg; + border: 2px outset $win95-bg; + border-top-width:0px; +} + +.navigation-bar { + color:black; +} + +.navigation-bar strong { + color:black; + font-weight:bold; +} + +.autosuggest-textarea__textarea, .spoiler-input__input { + border-radius:0px; + @include win95-border-slight-inset(); +} + +.autosuggest-textarea__textarea { + border-bottom:0px; +} + +.compose-form__uploads-wrapper { + border-radius:0px; + border-bottom:1px inset $win95-bg; + border-top-width:0px; +} + +.compose-form__upload-wrapper { + border-left:1px inset $win95-bg; + border-right:1px inset $win95-bg; +} + +.compose-form__buttons { + background-color:$win95-bg; + border-radius:0px; + box-shadow:unset; + border:2px groove $win95-bg; + margin-top:4px; + padding:4px 8px; +} + +.privacy-dropdown.active +.privacy-dropdown__value { + background: $win95-bg; + box-shadow:unset; +} + +.privacy-dropdown__option.active, .privacy-dropdown__option:hover, +.privacy-dropdown__option.active:hover { + background:$win95-window-header; +} + +.privacy-dropdown.active .privacy-dropdown__dropdown { + box-shadow:unset; + color:black; + @include win95-outset() + background: $win95-bg; +} + +.privacy-dropdown__option__content { + color:black; +} + +.privacy-dropdown__option__content strong { + font-weight:bold; +} + +.compose-form__warning::before { + content:"Tip:"; + font-weight:bold; + display:block; + position:absolute; + top:-10px; + background-color:$win95-bg; + font-size:11px; + padding: 0px 5px; +} + +.compose-form__warning { + position:relative; + box-shadow:unset; + border:2px groove $win95-bg; + background-color:$win95-bg; + color:black; +} + +.compose-form__warning a { + color:blue; +} + +.compose-form__warning strong { + color:black; + text-decoration:underline; +} + +.compose-form__buttons button.active:last-child { + @include win95-border-inset(); + background: #dfdfdf; + color:#7f7f7f; +} + +.compose-form__upload-thumbnail { + border-radius:0px; + border:2px groove $win95-bg; + background-color:$win95-bg; + padding:2px; + box-sizing:border-box; +} + +.compose-form__upload-thumbnail .icon-button { + max-width:20px; + max-height:20px; + line-height:10px !important; +} + +.compose-form__upload-thumbnail .icon-button::before { + content:"X"; + font-size:13px; + font-weight:bold; + color:black; +} + +.compose-form__upload-thumbnail .icon-button i { + display:none; +} + +.emoji-dialog.with-search { + box-shadow:unset; + border-radius:0px; + background-color:$win95-bg; + border:1px solid black; + box-sizing:content-box; + +} + +.emoji-dialog .emoji-search { + color:black; + background-color:white; + border-radius:0px; + @include win95-inset(); +} + +.emoji-dialog .emoji-search-wrapper { + border-bottom:2px groove $win95-bg; +} + +.emoji-dialog .emoji-category-title { + color:black; + font-weight:bold; +} + +.reply-indicator { + background-color:$win95-bg; + border-radius:3px; + border:2px groove $win95-bg; +} + +.button { + background-color:$win95-bg; + @include win95-outset() + border-radius:0px; + color:black; + font-weight:bold; +} + +.button:hover, .button:focus { + background-color:$win95-bg; +} + +.button:active { + @include win95-inset(); +} + +#Getting-started { + background-color:$win95-bg; + @include win95-inset(); + border-bottom-width:0px; +} + +#Getting-started::before { + content:"Start"; + color:black; + font-weight:bold; + font-size:15px; + width:80%; + text-align:center; + display:block; + position:absolute; + right:2px; +} + +#Getting-started { + position:relative; + padding:5px 15px; + width:60px; + font-size:0px; + color:$win95-bg; + + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAOCAIAAACpTQvdAAAACXBIWXMAAC4jAAAuIwF4pT92AAAAF3pUWHRBdXRob3IAAAiZS84oys9LzAEAC5oC6A7BY/IAAACWSURBVCiRhVJJDsQgDEuqOfRZ7a1P5gbP4uaJaEjTADMWQhHYjlk4p0wLnNdptdF4KvBUDyGzVwc2xO+uKtH+1o0ytEEmqFpuxlvFCGCxKbNIT56QCi2MzaA/2Mz+mERSOeqzJG2RUxkjdTabgPtFoZ1bZxcKvgPcLZVufAyR9Ni8v5dWDzfFx0giC1RvZFv6l35QQ/Mvv39XXgGzQpoAAAAASUVORK5CYII="); + background-repeat:no-repeat; + background-position:8%; + background-clip:padding-box; + background-size:auto 50%; +} + +.column-subheading { + background-color:$win95-bg; + color:black; + font-size:0px; + border-bottom: 2px groove $win95-bg; + padding:0px; + margin:0px; +} + +.column-link { + background-color:transparent; + background-size:32px 32px; + background-repeat:no-repeat; + background-position: 36px 50%; + color:black; + padding-left:40px; + + &:hover { + background-color: $win95-window-header; + background-size:32px 32px; + background-repeat:no-repeat; + background-position: 36px 50%; + color:white; + } + + i { + font-size: 0px; + width:32px; + } +} + +.column-link[href="/web/timelines/public"] { + background-image: url("../images/icon_public.png"); + &:hover { background-image: url("../images/icon_public.png"); } +} +.column-link[href="/web/timelines/public/local"] { + background-image: url("../images/icon_local.png"); + &:hover { background-image: url("../images/icon_local.png"); } +} +.column-link[href="/web/pinned"] { + background-image: url("../images/icon_pin.png"); + &:hover { background-image: url("../images/icon_pin.png"); } +} +.column-link[href="/web/favourites"] { + background-image: url("../images/icon_likes.png"); + &:hover { background-image: url("../images/icon_likes.png"); } +} +.column-link[href="/web/blocks"] { + background-image: url("../images/icon_blocks.png"); + &:hover { background-image: url("../images/icon_blocks.png"); } +} +.column-link[href="/web/mutes"] { + background-image: url("../images/icon_mutes.png"); + &:hover { background-image: url("../images/icon_mutes.png"); } +} +.column-link[href="/settings/preferences"] { + background-image: url("../images/icon_settings.png"); + &:hover { background-image: url("../images/icon_settings.png"); } +} +.column-link[href="/about/more"] { + background-image: url("../images/icon_about.png"); + &:hover { background-image: url("../images/icon_about.png"); } +} +.column-link[href="/auth/sign_out"] { + background-image: url("../images/icon_logout.png"); + &:hover { background-image: url("../images/icon_logout.png"); } +} + +.getting-started__footer { + display:none; +} + +.getting-started__wrapper::before { + content:"Mastodon 95"; + font-weight:bold; + font-size:23px; + color:white; + line-height:30px; + padding-left:20px; + padding-right:40px; + + left:0px; + bottom:-30px; + display:block; + position:absolute; + background-color:#7f7f7f; + width:200%; + height:30px; + + -ms-transform: rotate(-90deg); + + -webkit-transform: rotate(-90deg); + transform: rotate(-90deg); + transform-origin:top left; +} + +.getting-started__wrapper { + @include win95-border-outset() + background-color:$win95-bg; +} + +.account__header { + background-color:#7f7f7f; +} + +.account__header .account__header__content { + color:white; +} + +.account__action-bar__tab > span { + color:black; + font-weight:bold; +} + +.account__action-bar__tab strong { + color:black; +} + +.account__action-bar { + border: unset; +} + +.account__action-bar__tab { + border: 1px outset $win95-bg; +} + +.account__action-bar__tab:active { + @include win95-inset(); +} + +.dropdown--active .dropdown__content > ul { + background:$win95-tooltip-yellow; + border-radius:0px; + border:1px solid black; + box-shadow:unset; +} + +.dropdown--active::after { + display:none; +} + +.dropdown--active .icon-button { + color:black; + @include win95-inset(); +} + +.dropdown--active .dropdown__content > ul > li > a { + background:transparent; +} + +.dropdown--active .dropdown__content > ul > li > a:hover { + background:transparent; + color:black; + text-decoration:underline; +} + +.dropdown__sep { + border-color:#7f7f7f; +} + +.detailed-status__action-bar-dropdown .dropdown--active .dropdown__content.dropdown__left { + left:unset; +} + +.dropdown > .icon-button, .detailed-status__button > .icon-button, +.status__action-bar > .icon-button, .star-icon i { + /* i don't know what's going on with the inline + styles someone should look at the react code */ + height: 25px !important; + width: 28px !important; + box-sizing: border-box; +} + +.status__action-bar-button .fa-floppy-o { + padding-top: 2px; +} + +.status__action-bar-dropdown { + position: relative; + top: -3px; +} + +.detailed-status__action-bar-dropdown .dropdown { + position: relative; + top: -4px; +} + +.notification .status__action-bar { + border-bottom: none; +} + +.notification .status { + margin-bottom: 4px; +} + +.status__wrapper .status { + margin-bottom: 3px; +} + +.status__wrapper { + margin-bottom: 8px; +} + +.icon-button .fa-retweet { + position: relative; + top: -1px; +} + +.embed-modal, .error-modal, .onboarding-modal, +.actions-modal, .boost-modal, .confirmation-modal, .report-modal { + @include win95-outset() + background:$win95-bg; +} + +.actions-modal::before, +.boost-modal::before, +.confirmation-modal::before, +.report-modal::before { + content: "Confirmation"; + display:block; + background:$win95-window-header; + color:white; + font-weight:bold; + padding-left:2px; +} + +.boost-modal::before { + content: "Boost confirmation"; +} + +.boost-modal__action-bar > div > span:before { + content: "Tip: "; + font-weight:bold; +} + +.boost-modal__action-bar, .confirmation-modal__action-bar, .report-modal__action-bar { + background:$win95-bg; + margin-top:-15px; +} + +.embed-modal h4, .error-modal h4, .onboarding-modal h4 { + background:$win95-window-header; + color:white; + font-weight:bold; + padding:2px; + font-size:13px; + text-align:left; +} + +.confirmation-modal__action-bar { + .confirmation-modal__cancel-button { + color:black; + + &:active, + &:focus, + &:hover { + color:black; + } + + &:active { + @include win95-inset(); + } + } +} + +.embed-modal .embed-modal__container .embed-modal__html, +.embed-modal .embed-modal__container .embed-modal__html:focus { + background:white; + color:black; + @include win95-inset(); +} + +.modal-root__overlay, +.account__header > div { + background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAACXBIWXMAAC4jAAAuIwF4pT92AAAAFnpUWHRUaXRsZQAACJnLzU9JzElKBwALgwLXaCRlPwAAABd6VFh0QXV0aG9yAAAImUvOKMrPS8wBAAuaAugOwWPyAAAAEUlEQVQImWNgYGD4z4AE/gMADwMB/414xEUAAAAASUVORK5CYII='); +} + + +.admin-wrapper::before { + position:absolute; + top:0px; + content:"Control Panel"; + color:white; + background-color:$win95-window-header; + font-size:13px; + font-weight:bold; + width:calc(100%); + margin: 2px; + display:block; + padding:2px; + padding-left:22px; + box-sizing:border-box; +} + +.admin-wrapper { + position:relative; + background: $win95-bg; + @include win95-outset() + width:70vw; + height:80vh; + margin:10vh auto; + color: black; + padding-top:24px; + flex-direction:column; + overflow:hidden; +} + +.admin-wrapper .sidebar-wrapper { + position:static; + height:auto; + flex: 0 0 auto; + margin:2px; +} + +.admin-wrapper .content-wrapper { + flex: 1 1 auto; + width:calc(100% - 20px); + @include win95-border-outset() + position:relative; + margin-left:10px; + margin-right:10px; + margin-bottom:40px; + box-sizing:border-box; +} + +.admin-wrapper .content { + background-color: $win95-bg; + width: 100%; + max-width:100%; + min-height:100%; + box-sizing:border-box; + position:relative; +} + +.admin-wrapper .sidebar { + position:static; + background: $win95-bg; + color:black; + width: 100%; + height:auto; + padding-bottom: 20px; +} + +.admin-wrapper .sidebar .logo { + position:absolute; + top:2px; + left:4px; + width:18px; + height:18px; + margin:0px; +} + +.admin-wrapper .sidebar > ul { + background: $win95-bg; + margin:0px; + margin-left:8px; + color:black; + + & > li { + display:inline-block; + + &#settings, + &#admin { + padding:2px; + border: 0px solid transparent; + } + + &#logout { + position:absolute; + @include win95-outset(); + right:12px; + bottom:10px; + } + + &#web { + display:inline-block; + @include win95-outset(); + position:absolute; + left: 12px; + bottom: 10px; + } + + & > a { + display:inline-block; + @include win95-tab(); + padding:2px 5px; + margin:0px; + color:black; + vertical-align:baseline; + + &.selected { + background: $win95-bg; + color:black; + padding-top: 4px; + padding-bottom:4px; + } + + &:hover { + background: $win95-bg; + color:black; + } + } + + & > ul { + width:calc(100% - 20px); + background: transparent; + position:absolute; + left: 10px; + top:54px; + z-index:3; + + & > li { + background: $win95-bg; + display: inline-block; + vertical-align:baseline; + + & > a { + background: $win95-bg; + @include win95-tab(); + color:black; + padding:2px 5px; + position:relative; + z-index:3; + + &.selected { + background: $win95-bg; + color:black; + padding-bottom:4px; + padding-top: 4px; + padding-right:7px; + margin-left:-2px; + margin-right:-2px; + position:relative; + z-index:4; + + &:first-child { + margin-left:0px; + } + + &:hover { + background: transparent; + color:black; + } + } + + &:hover { + background: $win95-bg; + color:black; + } + } + } + } + } +} + +.flash-message { + background-color:$win95-tooltip-yellow; + color:black; + border:1px solid black; + border-radius:0px; + position:absolute; + top:0px; + left:0px; + width:100%; +} + +.admin-wrapper table { + background-color: white; + @include win95-border-slight-inset(); +} + +.admin-wrapper .content h2, +.simple_form .input.with_label .label_input > label, +.admin-wrapper .content h6, +.admin-wrapper .content > p, +.admin-wrapper .content .muted-hint, +.simple_form span.hint, +.simple_form .check_boxes .checkbox label, +.simple_form .input.with_label.boolean .label_input > label, +.filters .filter-subset a, +.simple_form .input.radio_buttons .radio label, +a.table-action-link, +.simple_form .input.with_block_label > label, +.simple_form p.hint { + color:black; +} + +.table > tbody > tr:nth-child(2n+1) > td, +.table > tbody > tr:nth-child(2n+1) > th { + background-color:white; +} + +.simple_form input[type=text], +.simple_form input[type=number], +.simple_form input[type=email], +.simple_form input[type=password], +.simple_form textarea { + color:black; + background-color:white; + @include win95-border-slight-inset(); +} + +.simple_form button, +.simple_form .button, +.simple_form .block-button +{ + background: $win95-bg; + @include win95-outset() + color:black; + font-weight: normal; + + &:hover { + background: $win95-bg; + } +} + +.simple_form button.negative, +.simple_form .button.negative, +.simple_form .block-button.negative +{ + background: $win95-bg; +} + +.filters .filter-subset { + border: 2px groove $win95-bg; + padding:2px; +} + +.filters .filter-subset a::before { + content: ""; + background-color:white; + border-radius:50%; + border:2px solid black; + border-top-color:#7f7f7f; + border-left-color:#7f7f7f; + border-bottom-color:#f5f5f5; + border-right-color:#f5f5f5; + width:12px; + height:12px; + display:inline-block; + vertical-align:middle; + margin-right:2px; +} + +.filters .filter-subset a.selected::before { + background-color:black; + box-shadow: inset 0 0 0 3px white; +} + +.filters .filter-subset a, +.filters .filter-subset a:hover, +.filters .filter-subset a.selected { + color:black; + border-bottom: 0px solid transparent; +} + -- cgit From c23844418839eea6a0f4c42e146683643996232b Mon Sep 17 00:00:00 2001 From: Andrew Date: Tue, 14 Nov 2017 01:22:48 -0800 Subject: Updates and fixes to win95 theme --- app/javascript/styles/win95.scss | 219 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 206 insertions(+), 13 deletions(-) (limited to 'app/javascript/styles') diff --git a/app/javascript/styles/win95.scss b/app/javascript/styles/win95.scss index 994fab4bf..885837b53 100644 --- a/app/javascript/styles/win95.scss +++ b/app/javascript/styles/win95.scss @@ -1,6 +1,12 @@ $win95-bg: #bfbfbf; +$win95-dark-grey: #404040; +$win95-mid-grey: #808080; $win95-window-header: #00007f; $win95-tooltip-yellow: #ffffcc; +$win95-blue: blue; + +$ui-base-lighter-color: $win95-dark-grey; +$ui-highlight-color: $win95-window-header; @mixin win95-border-outset() { border-left: 2px solid #efefef; @@ -67,6 +73,53 @@ $win95-tooltip-yellow: #ffffcc; @import 'application'; +/* borrowed from cybrespace style: wider columns and full column width images */ + +@media screen and (min-width: 1300px) { + .column { + flex-grow: 1 !important; + max-width: 400px; + } + + .drawer { + width: 17%; + max-width: 400px; + min-width: 330px; + } +} + +.media-gallery, +.video-player { + max-height:30vh; + height:30vh !important; + position:relative; + margin-top:20px; + margin-left:-68px; + width: calc(100% + 80px) !important; + max-width: calc(100% + 80px); +} + +.detailed-status .media-gallery, +.detailed-status .video-player { + margin-left:-5px; + width: calc(100% + 9px); + max-width: calc(100% + 9px); +} + +.video-player video { + transform: unset; + top: unset; +} + +.detailed-status .media-spoiler, +.status .media-spoiler { + height: 100%!important; + vertical-align: middle; +} + + +/* main win95 style */ + body { font-size:13px; font-family: "MS Sans Serif", "premillenium", sans-serif; @@ -417,15 +470,35 @@ body.admin { .status__action-bar-dropdown { margin-left:auto; margin-right:10px; + + .icon-button { + min-width:28px; + } } .status.light .status__content a { color:blue; } +.focusable:focus { + background: $win95-bg; + .detailed-status__action-bar { + background: $win95-bg; + } + + .status, .detailed-status { + background: white; + outline:2px dotted $win95-mid-grey; + } +} + .dropdown__trigger.icon-button { padding-right:6px; } +.detailed-status__action-bar-dropdown .icon-button { + min-width:28px; +} + .detailed-status { background:white; background-clip:padding-box; @@ -464,12 +537,11 @@ body.admin { @include win95-border-outset() padding:0px 0px 0px 0px; margin-right:4px; -} -.icon-button, -.icon-button.inverted, -.icon-button:hover, -.icon-button.inverted:hover { + color:#3f3f3f; + &.inverted, &:hover, &.inverted:hover, &:active, &:focus { + color:#3f3f3f; + } } .icon-button:active { @@ -487,6 +559,13 @@ body.admin { border:none; } +.icon-button.star-icon.active { + color: $gold-star; + &:active, &:hover, &:focus { + color: $gold-star; + } +} + .icon-button.star-icon > i { background:$win95-bg; @include win95-border-outset() @@ -497,6 +576,10 @@ body.admin { @include win95-border-inset(); } +.text-icon-button { + color:$win95-dark-grey; +} + .detailed-status__action-bar-dropdown { margin-left:auto; justify-content:right; @@ -672,6 +755,20 @@ body.admin { background-color:white; } +.search-popout { + box-shadow: unset; + color:black; + border-radius:0px; + background-color:$win95-tooltip-yellow; + border:1px solid black; + + h4 { + color:black; + text-transform: none; + font-weight:bold; + } +} + .search-results__header { background-color: $win95-bg; color:black; @@ -690,6 +787,18 @@ body.admin { color:white; } +.search__icon .fa { + color:#808080; + + &.active { + opacity:1.0; + } + + &:hover { + color: #808080; + } +} + .drawer__inner, .drawer__inner.darker { background-color:$win95-bg; @@ -857,14 +966,24 @@ body.admin { border-radius:0px; color:black; font-weight:bold; -} -.button:hover, .button:focus { - background-color:$win95-bg; -} + &:hover, &:focus, &:disabled { + background-color:$win95-bg; + } + + &:active { + @include win95-inset(); + } + + &:disabled { + color: #808080; + text-shadow: 1px 1px 0px #efefef; + + &:active { + @include win95-outset(); + } + } -.button:active { - @include win95-inset(); } #Getting-started { @@ -1029,13 +1148,18 @@ body.admin { @include win95-inset(); } -.dropdown--active .dropdown__content > ul { +.dropdown--active .dropdown__content > ul, +.dropdown-menu { background:$win95-tooltip-yellow; border-radius:0px; border:1px solid black; box-shadow:unset; } +.dropdown-menu a { + background-color:transparent; +} + .dropdown--active::after { display:none; } @@ -1055,7 +1179,9 @@ body.admin { text-decoration:underline; } -.dropdown__sep { +.dropdown__sep, +.dropdown-menu__separator +{ border-color:#7f7f7f; } @@ -1206,6 +1332,23 @@ body.admin { overflow:hidden; } +@media screen and (max-width: 1120px) { + .admin-wrapper { + width:90vw; + height:95vh; + margin:2.5vh auto; + } +} + +@media screen and (max-width: 740px) { + .admin-wrapper { + width:100vw; + height:95vh; + height:calc(100vh - 24px); + margin:0px 0px 0px 0px; + } +} + .admin-wrapper .sidebar-wrapper { position:static; height:auto; @@ -1354,6 +1497,36 @@ body.admin { } } +@media screen and (max-width: 1520px) { + .admin-wrapper .sidebar > ul > li > ul { + max-width:1000px; + } + + .admin-wrapper .sidebar { + padding-bottom: 45px; + } +} + +@media screen and (max-width: 600px) { + .admin-wrapper .sidebar > ul > li > ul { + max-width:500px; + } + + .admin-wrapper { + .sidebar { + padding:0px; + padding-bottom: 70px; + width: 100%; + height: auto; + } + .content-wrapper { + overflow:auto; + height:80%; + height:calc(100% - 150px); + } + } +} + .flash-message { background-color:$win95-tooltip-yellow; color:black; @@ -1376,11 +1549,13 @@ body.admin { .admin-wrapper .content > p, .admin-wrapper .content .muted-hint, .simple_form span.hint, +.simple_form h4, .simple_form .check_boxes .checkbox label, .simple_form .input.with_label.boolean .label_input > label, .filters .filter-subset a, .simple_form .input.radio_buttons .radio label, a.table-action-link, +a.table-action-link:hover, .simple_form .input.with_block_label > label, .simple_form p.hint { color:black; @@ -1399,6 +1574,10 @@ a.table-action-link, color:black; background-color:white; @include win95-border-slight-inset(); + + &:active, &:focus { + background-color:white; + } } .simple_form button, @@ -1415,6 +1594,20 @@ a.table-action-link, } } +.simple_form .warning, .table-form .warning +{ + background: $win95-tooltip-yellow; + color:black; + box-shadow: unset; + text-shadow:unset; + border:1px solid black; + + a { + color: blue; + text-decoration:underline; + } +} + .simple_form button.negative, .simple_form .button.negative, .simple_form .block-button.negative -- cgit From bdbbd06dad298dc3e1a5f568f4a3ff3635b48f22 Mon Sep 17 00:00:00 2001 From: kibigo! Date: Mon, 20 Nov 2017 22:13:37 -0800 Subject: Finalized theme loading and stuff --- app/controllers/about_controller.rb | 5 + app/controllers/accounts_controller.rb | 1 + app/controllers/admin/base_controller.rb | 7 +- app/controllers/application_controller.rb | 75 ++++++++++++-- app/controllers/auth/confirmations_controller.rb | 7 ++ app/controllers/auth/passwords_controller.rb | 5 + app/controllers/auth/registrations_controller.rb | 5 + app/controllers/auth/sessions_controller.rb | 5 + app/controllers/authorize_follows_controller.rb | 5 + app/controllers/follower_accounts_controller.rb | 4 +- app/controllers/following_accounts_controller.rb | 4 +- app/controllers/home_controller.rb | 5 + app/controllers/remote_follow_controller.rb | 5 + .../settings/applications_controller.rb | 4 +- app/controllers/settings/base_controller.rb | 12 +++ app/controllers/settings/deletes_controller.rb | 4 +- app/controllers/settings/exports_controller.rb | 6 +- .../settings/follower_domains_controller.rb | 6 +- app/controllers/settings/imports_controller.rb | 5 +- .../settings/keyword_mutes_controller.rb | 5 +- .../settings/notifications_controller.rb | 6 +- app/controllers/settings/preferences_controller.rb | 6 +- app/controllers/settings/profiles_controller.rb | 5 +- app/controllers/settings/sessions_controller.rb | 1 + .../two_factor_authentications_controller.rb | 5 +- app/controllers/shares_controller.rb | 5 + app/controllers/statuses_controller.rb | 2 + app/controllers/stream_entries_controller.rb | 1 + app/controllers/tags_controller.rb | 1 + app/javascript/core/about.js | 1 - app/javascript/core/embed.js | 2 +- app/javascript/core/home.js | 1 - app/javascript/core/public.js | 24 +++++ app/javascript/core/settings.js | 80 +++++---------- app/javascript/core/share.js | 1 - app/javascript/core/theme.yml | 14 +++ app/javascript/locales/index.js | 9 ++ app/javascript/mastodon/locales/index.js | 10 +- app/javascript/packs/about.js | 22 ++++ app/javascript/packs/application.js | 10 -- app/javascript/packs/common.js | 3 + app/javascript/packs/public.js | 75 ++++++++++++++ app/javascript/packs/share.js | 22 ++++ app/javascript/styles/common.scss | 5 - app/javascript/styles/win95.scss | 113 +++++++++++---------- .../themes/glitch/containers/mastodon.js | 2 +- app/javascript/themes/glitch/packs/common.js | 4 +- app/javascript/themes/glitch/packs/home.js | 4 +- app/javascript/themes/glitch/packs/public.js | 83 +-------------- app/javascript/themes/glitch/theme.yml | 39 ++++--- app/javascript/themes/vanilla/theme.yml | 35 ++++--- app/javascript/themes/win95/index.js | 10 ++ app/javascript/themes/win95/theme.yml | 23 +++++ app/lib/themes.rb | 12 +++ app/views/about/more.html.haml | 1 - app/views/about/show.html.haml | 1 - app/views/admin/reports/show.html.haml | 3 - app/views/admin/statuses/index.html.haml | 3 - app/views/home/index.html.haml | 6 -- app/views/layouts/_theme.html.haml | 10 ++ app/views/layouts/admin.html.haml | 3 - app/views/layouts/application.html.haml | 10 +- app/views/layouts/auth.html.haml | 3 - app/views/layouts/embedded.html.haml | 7 +- app/views/layouts/error.html.haml | 4 +- app/views/layouts/modal.html.haml | 3 - app/views/layouts/public.html.haml | 3 - app/views/shares/show.html.haml | 1 - app/views/tags/show.html.haml | 1 - config/webpack/configuration.js | 13 ++- config/webpack/generateLocalePacks.js | 2 +- config/webpack/shared.js | 40 ++++---- config/webpacker.yml | 12 --- 73 files changed, 566 insertions(+), 371 deletions(-) create mode 100644 app/controllers/settings/base_controller.rb delete mode 100644 app/javascript/core/about.js delete mode 100644 app/javascript/core/home.js delete mode 100644 app/javascript/core/share.js create mode 100644 app/javascript/core/theme.yml create mode 100644 app/javascript/locales/index.js create mode 100644 app/javascript/packs/about.js create mode 100644 app/javascript/packs/common.js create mode 100644 app/javascript/packs/public.js create mode 100644 app/javascript/packs/share.js delete mode 100644 app/javascript/styles/common.scss create mode 100644 app/javascript/themes/win95/index.js create mode 100644 app/javascript/themes/win95/theme.yml create mode 100644 app/views/layouts/_theme.html.haml (limited to 'app/javascript/styles') diff --git a/app/controllers/about_controller.rb b/app/controllers/about_controller.rb index 47690e81e..8785df14e 100644 --- a/app/controllers/about_controller.rb +++ b/app/controllers/about_controller.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true class AboutController < ApplicationController + before_action :set_pack before_action :set_body_classes before_action :set_instance_presenter, only: [:show, :more, :terms] @@ -21,6 +22,10 @@ class AboutController < ApplicationController helper_method :new_user + def set_pack + use_pack action_name == 'show' ? 'about' : 'common' + end + def set_instance_presenter @instance_presenter = InstancePresenter.new end diff --git a/app/controllers/accounts_controller.rb b/app/controllers/accounts_controller.rb index 75915b337..309cb65da 100644 --- a/app/controllers/accounts_controller.rb +++ b/app/controllers/accounts_controller.rb @@ -7,6 +7,7 @@ class AccountsController < ApplicationController def show respond_to do |format| format.html do + use_pack 'public' @pinned_statuses = [] if current_account && @account.blocking?(current_account) diff --git a/app/controllers/admin/base_controller.rb b/app/controllers/admin/base_controller.rb index db4839a8f..726134509 100644 --- a/app/controllers/admin/base_controller.rb +++ b/app/controllers/admin/base_controller.rb @@ -4,8 +4,13 @@ module Admin class BaseController < ApplicationController include Authorization + layout 'admin' + before_action :require_staff! + before_action :set_pack - layout 'admin' + def set_pack + use_pack 'admin' + end end end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index f5dbe837e..7cc4eea27 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -12,8 +12,6 @@ class ApplicationController < ActionController::Base helper_method :current_account helper_method :current_session - helper_method :current_theme - helper_method :theme_data helper_method :single_user_mode? rescue_from ActionController::RoutingError, with: :not_found @@ -54,6 +52,69 @@ class ApplicationController < ActionController::Base new_user_session_path end + def pack(data, pack_name) + return nil unless pack?(data, pack_name) + pack_data = { + common: pack_name == 'common' ? nil : resolve_pack(data['name'] ? Themes.instance.get(current_theme) : Themes.instance.core, 'common'), + name: data['name'], + pack: pack_name, + preload: nil, + stylesheet: false + } + if data['pack'][pack_name].is_a?(Hash) + pack_data[:common] = nil if data['pack'][pack_name]['use_common'] == false + pack_data[:pack] = nil unless data['pack'][pack_name]['filename'] + if data['pack'][pack_name]['preload'] + pack_data[:preload] = [data['pack'][pack_name]['preload']] if data['pack'][pack_name]['preload'].is_a?(String) + pack_data[:preload] = data['pack'][pack_name]['preload'] if data['pack'][pack_name]['preload'].is_a?(Array) + end + pack_data[:stylesheet] = true if data['pack'][pack_name]['stylesheet'] + end + pack_data + end + + def pack?(data, pack_name) + if data['pack'].is_a?(Hash) && data['pack'].key?(pack_name) + return true if data['pack'][pack_name].is_a?(String) || data['pack'][pack_name].is_a?(Hash) + end + false + end + + def nil_pack(data, pack_name) + { + common: pack_name == 'common' ? nil : resolve_pack(data['name'] ? Themes.instance.get(current_theme) : Themes.instance.core, 'common'), + name: data['name'], + pack: nil, + preload: nil, + stylesheet: false + } + end + + def resolve_pack(data, pack_name) + result = pack(data, pack_name) + unless result + if data['name'] && data.key?('fallback') + if data['fallback'].nil? + return nil_pack(data, pack_name) + elsif data['fallback'].is_a?(String) && Themes.instance.get(data['fallback']) + return resolve_pack(Themes.instance.get(data['fallback']), pack_name) + elsif data['fallback'].is_a?(Array) + data['fallback'].each do |fallback| + return resolve_pack(Themes.instance.get(fallback), pack_name) if Themes.instance.get(fallback) + end + end + return nil_pack(data, pack_name) + end + return data.key?('name') && data['name'] != default_theme ? resolve_pack(Themes.instance.get(default_theme), pack_name) : nil_pack(data, pack_name) + end + result + end + + def use_pack(pack_name) + @core = resolve_pack(Themes.instance.core, pack_name) + @theme = resolve_pack(Themes.instance.get(current_theme), pack_name) + end + protected def forbidden @@ -84,13 +145,13 @@ class ApplicationController < ActionController::Base @current_session ||= SessionActivation.find_by(session_id: cookies.signed['_session_id']) end - def current_theme - return Setting.default_settings['theme'] unless Themes.instance.names.include? current_user&.setting_theme - current_user.setting_theme + def default_theme + Setting.default_settings['theme'] end - def theme_data - Themes.instance.get(current_theme) + def current_theme + return default_theme unless Themes.instance.names.include? current_user&.setting_theme + current_user.setting_theme end def cache_collection(raw, klass) diff --git a/app/controllers/auth/confirmations_controller.rb b/app/controllers/auth/confirmations_controller.rb index d5e8e58ed..5ffa1c9a3 100644 --- a/app/controllers/auth/confirmations_controller.rb +++ b/app/controllers/auth/confirmations_controller.rb @@ -2,10 +2,17 @@ class Auth::ConfirmationsController < Devise::ConfirmationsController layout 'auth' + before_action :set_pack def show super do |user| BootstrapTimelineWorker.perform_async(user.account_id) if user.errors.empty? end end + + private + + def set_pack + use_pack 'auth' + end end diff --git a/app/controllers/auth/passwords_controller.rb b/app/controllers/auth/passwords_controller.rb index 171b997dc..e0400aa3d 100644 --- a/app/controllers/auth/passwords_controller.rb +++ b/app/controllers/auth/passwords_controller.rb @@ -2,6 +2,7 @@ class Auth::PasswordsController < Devise::PasswordsController before_action :check_validity_of_reset_password_token, only: :edit + before_action :set_pack layout 'auth' @@ -17,4 +18,8 @@ class Auth::PasswordsController < Devise::PasswordsController def reset_password_token_is_valid? resource_class.with_reset_password_token(params[:reset_password_token]).present? end + + def set_pack + use_pack 'auth' + end end diff --git a/app/controllers/auth/registrations_controller.rb b/app/controllers/auth/registrations_controller.rb index 223db96ff..cc7a69ab0 100644 --- a/app/controllers/auth/registrations_controller.rb +++ b/app/controllers/auth/registrations_controller.rb @@ -5,6 +5,7 @@ class Auth::RegistrationsController < Devise::RegistrationsController before_action :check_enabled_registrations, only: [:new, :create] before_action :configure_sign_up_params, only: [:create] + before_action :set_path before_action :set_sessions, only: [:edit, :update] before_action :set_instance_presenter, only: [:new, :create, :update] @@ -40,6 +41,10 @@ class Auth::RegistrationsController < Devise::RegistrationsController private + def set_pack + use_pack %w(edit update).include?(action_name) ? 'admin' : 'auth' + end + def set_instance_presenter @instance_presenter = InstancePresenter.new end diff --git a/app/controllers/auth/sessions_controller.rb b/app/controllers/auth/sessions_controller.rb index a5acb6c36..72d544102 100644 --- a/app/controllers/auth/sessions_controller.rb +++ b/app/controllers/auth/sessions_controller.rb @@ -9,6 +9,7 @@ class Auth::SessionsController < Devise::SessionsController skip_before_action :check_suspension, only: [:destroy] prepend_before_action :authenticate_with_two_factor, if: :two_factor_enabled?, only: [:create] before_action :set_instance_presenter, only: [:new] + before_action :set_pack def create super do |resource| @@ -85,6 +86,10 @@ class Auth::SessionsController < Devise::SessionsController private + def set_pack + use_pack 'auth' + end + def set_instance_presenter @instance_presenter = InstancePresenter.new end diff --git a/app/controllers/authorize_follows_controller.rb b/app/controllers/authorize_follows_controller.rb index 78b564183..2d29bd379 100644 --- a/app/controllers/authorize_follows_controller.rb +++ b/app/controllers/authorize_follows_controller.rb @@ -4,6 +4,7 @@ class AuthorizeFollowsController < ApplicationController layout 'modal' before_action :authenticate_user! + before_action :set_pack def show @account = located_account || render(:error) @@ -23,6 +24,10 @@ class AuthorizeFollowsController < ApplicationController private + def set_pack + use_pack 'modal' + end + def follow_attempt FollowService.new.call(current_account, acct_without_prefix) end diff --git a/app/controllers/follower_accounts_controller.rb b/app/controllers/follower_accounts_controller.rb index 399e79665..080cbde11 100644 --- a/app/controllers/follower_accounts_controller.rb +++ b/app/controllers/follower_accounts_controller.rb @@ -7,7 +7,9 @@ class FollowerAccountsController < ApplicationController @follows = Follow.where(target_account: @account).recent.page(params[:page]).per(FOLLOW_PER_PAGE).preload(:account) respond_to do |format| - format.html + format.html do + use_pack 'public' + end format.json do render json: collection_presenter, diff --git a/app/controllers/following_accounts_controller.rb b/app/controllers/following_accounts_controller.rb index 1e73d4bd4..74e83ad81 100644 --- a/app/controllers/following_accounts_controller.rb +++ b/app/controllers/following_accounts_controller.rb @@ -7,7 +7,9 @@ class FollowingAccountsController < ApplicationController @follows = Follow.where(account: @account).recent.page(params[:page]).per(FOLLOW_PER_PAGE).preload(:target_account) respond_to do |format| - format.html + format.html do + use_pack 'public' + end format.json do render json: collection_presenter, diff --git a/app/controllers/home_controller.rb b/app/controllers/home_controller.rb index 21dde20ce..7437a647e 100644 --- a/app/controllers/home_controller.rb +++ b/app/controllers/home_controller.rb @@ -2,6 +2,7 @@ class HomeController < ApplicationController before_action :authenticate_user! + before_action :set_pack before_action :set_initial_state_json def index @@ -37,6 +38,10 @@ class HomeController < ApplicationController redirect_to(default_redirect_path) end + def set_pack + use_pack 'home' + end + def set_initial_state_json serializable_resource = ActiveModelSerializers::SerializableResource.new(InitialStatePresenter.new(initial_state_params), serializer: InitialStateSerializer) @initial_state_json = serializable_resource.to_json diff --git a/app/controllers/remote_follow_controller.rb b/app/controllers/remote_follow_controller.rb index 48b026aa5..e6f379886 100644 --- a/app/controllers/remote_follow_controller.rb +++ b/app/controllers/remote_follow_controller.rb @@ -4,6 +4,7 @@ class RemoteFollowController < ApplicationController layout 'modal' before_action :set_account + before_action :set_pack before_action :gone, if: :suspended_account? def new @@ -31,6 +32,10 @@ class RemoteFollowController < ApplicationController { acct: session[:remote_follow] } end + def set_pack + use_pack 'modal' + end + def set_account @account = Account.find_local!(params[:account_username]) end diff --git a/app/controllers/settings/applications_controller.rb b/app/controllers/settings/applications_controller.rb index 8fc9a0fa9..35a6f7f9e 100644 --- a/app/controllers/settings/applications_controller.rb +++ b/app/controllers/settings/applications_controller.rb @@ -1,9 +1,7 @@ # frozen_string_literal: true -class Settings::ApplicationsController < ApplicationController - layout 'admin' +class Settings::ApplicationsController < Settings::BaseController - before_action :authenticate_user! before_action :set_application, only: [:show, :update, :destroy, :regenerate] before_action :prepare_scopes, only: [:create, :update] diff --git a/app/controllers/settings/base_controller.rb b/app/controllers/settings/base_controller.rb new file mode 100644 index 000000000..7322d461b --- /dev/null +++ b/app/controllers/settings/base_controller.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +class Settings::BaseController < ApplicationController + layout 'admin' + + before_action :authenticate_user! + before_action :set_pack + + def set_pack + use_pack 'settings' + end +end diff --git a/app/controllers/settings/deletes_controller.rb b/app/controllers/settings/deletes_controller.rb index 80002b995..e4cb35a8e 100644 --- a/app/controllers/settings/deletes_controller.rb +++ b/app/controllers/settings/deletes_controller.rb @@ -1,10 +1,8 @@ # frozen_string_literal: true -class Settings::DeletesController < ApplicationController - layout 'admin' +class Settings::DeletesController < Settings::BaseController before_action :check_enabled_deletion - before_action :authenticate_user! def show @confirmation = Form::DeleteConfirmation.new diff --git a/app/controllers/settings/exports_controller.rb b/app/controllers/settings/exports_controller.rb index ae62f00c1..9c03ece86 100644 --- a/app/controllers/settings/exports_controller.rb +++ b/app/controllers/settings/exports_controller.rb @@ -1,10 +1,6 @@ # frozen_string_literal: true -class Settings::ExportsController < ApplicationController - layout 'admin' - - before_action :authenticate_user! - +class Settings::ExportsController < Settings::BaseController def show @export = Export.new(current_account) end diff --git a/app/controllers/settings/follower_domains_controller.rb b/app/controllers/settings/follower_domains_controller.rb index 9968504e5..141b2270d 100644 --- a/app/controllers/settings/follower_domains_controller.rb +++ b/app/controllers/settings/follower_domains_controller.rb @@ -2,11 +2,7 @@ require 'sidekiq-bulk' -class Settings::FollowerDomainsController < ApplicationController - layout 'admin' - - before_action :authenticate_user! - +class Settings::FollowerDomainsController < Settings::BaseController def show @account = current_account @domains = current_account.followers.reorder('MIN(follows.id) DESC').group('accounts.domain').select('accounts.domain, count(accounts.id) as accounts_from_domain').page(params[:page]).per(10) diff --git a/app/controllers/settings/imports_controller.rb b/app/controllers/settings/imports_controller.rb index 0db13d1ca..dbd136ebe 100644 --- a/app/controllers/settings/imports_controller.rb +++ b/app/controllers/settings/imports_controller.rb @@ -1,9 +1,6 @@ # frozen_string_literal: true -class Settings::ImportsController < ApplicationController - layout 'admin' - - before_action :authenticate_user! +class Settings::ImportsController < Settings::BaseController before_action :set_account def show diff --git a/app/controllers/settings/keyword_mutes_controller.rb b/app/controllers/settings/keyword_mutes_controller.rb index f79e1b320..699b8a3ef 100644 --- a/app/controllers/settings/keyword_mutes_controller.rb +++ b/app/controllers/settings/keyword_mutes_controller.rb @@ -1,9 +1,6 @@ # frozen_string_literal: true -class Settings::KeywordMutesController < ApplicationController - layout 'admin' - - before_action :authenticate_user! +class Settings::KeywordMutesController < Settings::BaseController before_action :load_keyword_mute, only: [:edit, :update, :destroy] def index diff --git a/app/controllers/settings/notifications_controller.rb b/app/controllers/settings/notifications_controller.rb index ce2530c54..6286e3ebf 100644 --- a/app/controllers/settings/notifications_controller.rb +++ b/app/controllers/settings/notifications_controller.rb @@ -1,10 +1,6 @@ # frozen_string_literal: true -class Settings::NotificationsController < ApplicationController - layout 'admin' - - before_action :authenticate_user! - +class Settings::NotificationsController < Settings::BaseController def show; end def update diff --git a/app/controllers/settings/preferences_controller.rb b/app/controllers/settings/preferences_controller.rb index 069026715..3aefd90a2 100644 --- a/app/controllers/settings/preferences_controller.rb +++ b/app/controllers/settings/preferences_controller.rb @@ -1,10 +1,6 @@ # frozen_string_literal: true -class Settings::PreferencesController < ApplicationController - layout 'admin' - - before_action :authenticate_user! - +class Settings::PreferencesController < Settings::BaseController def show; end def update diff --git a/app/controllers/settings/profiles_controller.rb b/app/controllers/settings/profiles_controller.rb index 28f78a4fb..dadc3d911 100644 --- a/app/controllers/settings/profiles_controller.rb +++ b/app/controllers/settings/profiles_controller.rb @@ -1,11 +1,8 @@ # frozen_string_literal: true -class Settings::ProfilesController < ApplicationController +class Settings::ProfilesController < Settings::BaseController include ObfuscateFilename - layout 'admin' - - before_action :authenticate_user! before_action :set_account obfuscate_filename [:account, :avatar] diff --git a/app/controllers/settings/sessions_controller.rb b/app/controllers/settings/sessions_controller.rb index 0da1b027b..780ea64b4 100644 --- a/app/controllers/settings/sessions_controller.rb +++ b/app/controllers/settings/sessions_controller.rb @@ -1,5 +1,6 @@ # frozen_string_literal: true +# Intentionally does not inherit from BaseController class Settings::SessionsController < ApplicationController before_action :set_session, only: :destroy diff --git a/app/controllers/settings/two_factor_authentications_controller.rb b/app/controllers/settings/two_factor_authentications_controller.rb index 863cc7351..8c7737e9d 100644 --- a/app/controllers/settings/two_factor_authentications_controller.rb +++ b/app/controllers/settings/two_factor_authentications_controller.rb @@ -1,10 +1,7 @@ # frozen_string_literal: true module Settings - class TwoFactorAuthenticationsController < ApplicationController - layout 'admin' - - before_action :authenticate_user! + class TwoFactorAuthenticationsController < BaseController before_action :verify_otp_required, only: [:create] def show diff --git a/app/controllers/shares_controller.rb b/app/controllers/shares_controller.rb index 994742c3d..81d279c8b 100644 --- a/app/controllers/shares_controller.rb +++ b/app/controllers/shares_controller.rb @@ -4,6 +4,7 @@ class SharesController < ApplicationController layout 'modal' before_action :authenticate_user! + before_action :set_pack before_action :set_body_classes def show @@ -24,6 +25,10 @@ class SharesController < ApplicationController } end + def set_pack + use_pack 'share' + end + def set_body_classes @body_classes = 'compose-standalone' end diff --git a/app/controllers/statuses_controller.rb b/app/controllers/statuses_controller.rb index e8a360fb5..84c9e7685 100644 --- a/app/controllers/statuses_controller.rb +++ b/app/controllers/statuses_controller.rb @@ -14,6 +14,7 @@ class StatusesController < ApplicationController def show respond_to do |format| format.html do + use_pack 'public' @ancestors = @status.reply? ? cache_collection(@status.ancestors(current_account), Status) : [] @descendants = cache_collection(@status.descendants(current_account), Status) @@ -37,6 +38,7 @@ class StatusesController < ApplicationController end def embed + use_pack 'embed' response.headers['X-Frame-Options'] = 'ALLOWALL' render 'stream_entries/embed', layout: 'embedded' end diff --git a/app/controllers/stream_entries_controller.rb b/app/controllers/stream_entries_controller.rb index 5f61e2182..b597ba4bb 100644 --- a/app/controllers/stream_entries_controller.rb +++ b/app/controllers/stream_entries_controller.rb @@ -14,6 +14,7 @@ class StreamEntriesController < ApplicationController def show respond_to do |format| format.html do + use_pack 'public' @ancestors = @stream_entry.activity.reply? ? cache_collection(@stream_entry.activity.ancestors(current_account), Status) : [] @descendants = cache_collection(@stream_entry.activity.descendants(current_account), Status) end diff --git a/app/controllers/tags_controller.rb b/app/controllers/tags_controller.rb index 9f3090e37..5d11a8139 100644 --- a/app/controllers/tags_controller.rb +++ b/app/controllers/tags_controller.rb @@ -9,6 +9,7 @@ class TagsController < ApplicationController respond_to do |format| format.html do + use_pack 'about' serializable_resource = ActiveModelSerializers::SerializableResource.new(InitialStatePresenter.new(initial_state_params), serializer: InitialStateSerializer) @initial_state_json = serializable_resource.to_json end diff --git a/app/javascript/core/about.js b/app/javascript/core/about.js deleted file mode 100644 index 6ed0e4ad3..000000000 --- a/app/javascript/core/about.js +++ /dev/null @@ -1 +0,0 @@ -// This file will be loaded on about pages, regardless of theme. diff --git a/app/javascript/core/embed.js b/app/javascript/core/embed.js index 8167706a3..6146e6592 100644 --- a/app/javascript/core/embed.js +++ b/app/javascript/core/embed.js @@ -13,7 +13,7 @@ window.addEventListener('message', e => { id: data.id, height: document.getElementsByTagName('html')[0].scrollHeight, }, '*'); - }); + }; if (['interactive', 'complete'].includes(document.readyState)) { setEmbedHeight(); diff --git a/app/javascript/core/home.js b/app/javascript/core/home.js deleted file mode 100644 index 3c2e01590..000000000 --- a/app/javascript/core/home.js +++ /dev/null @@ -1 +0,0 @@ -// This file will be loaded on home pages, regardless of theme. diff --git a/app/javascript/core/public.js b/app/javascript/core/public.js index 1a36b7a5f..47c34a259 100644 --- a/app/javascript/core/public.js +++ b/app/javascript/core/public.js @@ -1 +1,25 @@ // This file will be loaded on public pages, regardless of theme. + +const { delegate } = require('rails-ujs'); + +delegate(document, '.webapp-btn', 'click', ({ target, button }) => { + if (button !== 0) { + return true; + } + window.location.href = target.href; + return false; +}); + +delegate(document, '.status__content__spoiler-link', 'click', ({ target }) => { + const contentEl = target.parentNode.parentNode.querySelector('.e-content'); + + if (contentEl.style.display === 'block') { + contentEl.style.display = 'none'; + target.parentNode.style.marginBottom = 0; + } else { + contentEl.style.display = 'block'; + target.parentNode.style.marginBottom = null; + } + + return false; +}); diff --git a/app/javascript/core/settings.js b/app/javascript/core/settings.js index 91332ed5a..7fb1d8e77 100644 --- a/app/javascript/core/settings.js +++ b/app/javascript/core/settings.js @@ -1,65 +1,37 @@ // This file will be loaded on settings pages, regardless of theme. -function main() { - const { length } = require('stringz'); - const { delegate } = require('rails-ujs'); +const { length } = require('stringz'); +const { delegate } = require('rails-ujs'); - delegate(document, '.webapp-btn', 'click', ({ target, button }) => { - if (button !== 0) { - return true; - } - window.location.href = target.href; - return false; - }); +delegate(document, '.account_display_name', 'input', ({ target }) => { + const nameCounter = document.querySelector('.name-counter'); - delegate(document, '.status__content__spoiler-link', 'click', ({ target }) => { - const contentEl = target.parentNode.parentNode.querySelector('.e-content'); - - if (contentEl.style.display === 'block') { - contentEl.style.display = 'none'; - target.parentNode.style.marginBottom = 0; - } else { - contentEl.style.display = 'block'; - target.parentNode.style.marginBottom = null; - } - - return false; - }); - - delegate(document, '.account_display_name', 'input', ({ target }) => { - const nameCounter = document.querySelector('.name-counter'); - - if (nameCounter) { - nameCounter.textContent = 30 - length(target.value); - } - }); - - delegate(document, '.account_note', 'input', ({ target }) => { - const noteCounter = document.querySelector('.note-counter'); + if (nameCounter) { + nameCounter.textContent = 30 - length(target.value); + } +}); - if (noteCounter) { - const noteWithoutMetadata = processBio(target.value).text; - noteCounter.textContent = 500 - length(noteWithoutMetadata); - } - }); +delegate(document, '.account_note', 'input', ({ target }) => { + const noteCounter = document.querySelector('.note-counter'); - delegate(document, '#account_avatar', 'change', ({ target }) => { - const avatar = document.querySelector('.card.compact .avatar img'); - const [file] = target.files || []; - const url = file ? URL.createObjectURL(file) : avatar.dataset.originalSrc; + if (noteCounter) { + const noteWithoutMetadata = processBio(target.value).text; + noteCounter.textContent = 500 - length(noteWithoutMetadata); + } +}); - avatar.src = url; - }); +delegate(document, '#account_avatar', 'change', ({ target }) => { + const avatar = document.querySelector('.card.compact .avatar img'); + const [file] = target.files || []; + const url = file ? URL.createObjectURL(file) : avatar.dataset.originalSrc; - delegate(document, '#account_header', 'change', ({ target }) => { - const header = document.querySelector('.card.compact'); - const [file] = target.files || []; - const url = file ? URL.createObjectURL(file) : header.dataset.originalSrc; + avatar.src = url; +}); - header.style.backgroundImage = `url(${url})`; - }); -} +delegate(document, '#account_header', 'change', ({ target }) => { + const header = document.querySelector('.card.compact'); + const [file] = target.files || []; + const url = file ? URL.createObjectURL(file) : header.dataset.originalSrc; -loadPolyfills().then(main).catch(error => { - console.error(error); + header.style.backgroundImage = `url(${url})`; }); diff --git a/app/javascript/core/share.js b/app/javascript/core/share.js deleted file mode 100644 index 98a413632..000000000 --- a/app/javascript/core/share.js +++ /dev/null @@ -1 +0,0 @@ -// This file will be loaded on share pages, regardless of theme. diff --git a/app/javascript/core/theme.yml b/app/javascript/core/theme.yml new file mode 100644 index 000000000..17e8e66b3 --- /dev/null +++ b/app/javascript/core/theme.yml @@ -0,0 +1,14 @@ +# These packs will be loaded on every appropriate page, regardless of +# theme. +pack: + about: + admin: admin.js + auth: + common: common.js + embed: embed.js + error: + home: + modal: + public: public.js + settings: settings.js + share: diff --git a/app/javascript/locales/index.js b/app/javascript/locales/index.js new file mode 100644 index 000000000..421cb7fab --- /dev/null +++ b/app/javascript/locales/index.js @@ -0,0 +1,9 @@ +let theLocale; + +export function setLocale(locale) { + theLocale = locale; +} + +export function getLocale() { + return theLocale; +} diff --git a/app/javascript/mastodon/locales/index.js b/app/javascript/mastodon/locales/index.js index 421cb7fab..7e7297561 100644 --- a/app/javascript/mastodon/locales/index.js +++ b/app/javascript/mastodon/locales/index.js @@ -1,9 +1 @@ -let theLocale; - -export function setLocale(locale) { - theLocale = locale; -} - -export function getLocale() { - return theLocale; -} +export * from 'locales'; diff --git a/app/javascript/packs/about.js b/app/javascript/packs/about.js new file mode 100644 index 000000000..63e12da42 --- /dev/null +++ b/app/javascript/packs/about.js @@ -0,0 +1,22 @@ +import loadPolyfills from '../mastodon/load_polyfills'; + +function loaded() { + const TimelineContainer = require('../mastodon/containers/timeline_container').default; + const React = require('react'); + const ReactDOM = require('react-dom'); + const mountNode = document.getElementById('mastodon-timeline'); + + if (mountNode !== null) { + const props = JSON.parse(mountNode.getAttribute('data-props')); + ReactDOM.render(, mountNode); + } +} + +function main() { + const ready = require('../mastodon/ready').default; + ready(loaded); +} + +loadPolyfills().then(main).catch(error => { + console.error(error); +}); diff --git a/app/javascript/packs/application.js b/app/javascript/packs/application.js index ee5bf244c..116632dea 100644 --- a/app/javascript/packs/application.js +++ b/app/javascript/packs/application.js @@ -1,15 +1,5 @@ -// THIS IS THE `vanilla` THEME PACK FILE!! -// IT'S HERE FOR UPSTREAM COMPATIBILITY!! -// THE `glitch` PACK FILE IS IN `themes/glitch/index.js`!! - import loadPolyfills from '../mastodon/load_polyfills'; -// import default stylesheet with variables -import 'font-awesome/css/font-awesome.css'; -import '../styles/application.scss'; - -require.context('../images/', true); - loadPolyfills().then(() => { require('../mastodon/main').default(); }).catch(e => { diff --git a/app/javascript/packs/common.js b/app/javascript/packs/common.js new file mode 100644 index 000000000..f3156c1c6 --- /dev/null +++ b/app/javascript/packs/common.js @@ -0,0 +1,3 @@ +import 'font-awesome/css/font-awesome.css'; +import 'styles/application.scss' +require.context('../images/', true); diff --git a/app/javascript/packs/public.js b/app/javascript/packs/public.js new file mode 100644 index 000000000..3472af6c1 --- /dev/null +++ b/app/javascript/packs/public.js @@ -0,0 +1,75 @@ +import loadPolyfills from '../mastodon/load_polyfills'; +import ready from '../mastodon/ready'; + +function main() { + const IntlRelativeFormat = require('intl-relativeformat').default; + const emojify = require('../mastodon/features/emoji/emoji').default; + const { getLocale } = require('../mastodon/locales'); + const { localeData } = getLocale(); + const VideoContainer = require('../mastodon/containers/video_container').default; + const MediaGalleryContainer = require('../mastodon/containers/media_gallery_container').default; + const CardContainer = require('../mastodon/containers/card_container').default; + const React = require('react'); + const ReactDOM = require('react-dom'); + + localeData.forEach(IntlRelativeFormat.__addLocaleData); + + ready(() => { + const locale = document.documentElement.lang; + + const dateTimeFormat = new Intl.DateTimeFormat(locale, { + year: 'numeric', + month: 'long', + day: 'numeric', + hour: 'numeric', + minute: 'numeric', + }); + + const relativeFormat = new IntlRelativeFormat(locale); + + [].forEach.call(document.querySelectorAll('.emojify'), (content) => { + content.innerHTML = emojify(content.innerHTML); + }); + + [].forEach.call(document.querySelectorAll('time.formatted'), (content) => { + const datetime = new Date(content.getAttribute('datetime')); + const formattedDate = dateTimeFormat.format(datetime); + + content.title = formattedDate; + content.textContent = formattedDate; + }); + + [].forEach.call(document.querySelectorAll('time.time-ago'), (content) => { + const datetime = new Date(content.getAttribute('datetime')); + + content.title = dateTimeFormat.format(datetime); + content.textContent = relativeFormat.format(datetime); + }); + + [].forEach.call(document.querySelectorAll('.logo-button'), (content) => { + content.addEventListener('click', (e) => { + e.preventDefault(); + window.open(e.target.href, 'mastodon-intent', 'width=400,height=400,resizable=no,menubar=no,status=no,scrollbars=yes'); + }); + }); + + [].forEach.call(document.querySelectorAll('[data-component="Video"]'), (content) => { + const props = JSON.parse(content.getAttribute('data-props')); + ReactDOM.render(, content); + }); + + [].forEach.call(document.querySelectorAll('[data-component="MediaGallery"]'), (content) => { + const props = JSON.parse(content.getAttribute('data-props')); + ReactDOM.render(, content); + }); + + [].forEach.call(document.querySelectorAll('[data-component="Card"]'), (content) => { + const props = JSON.parse(content.getAttribute('data-props')); + ReactDOM.render(, content); + }); + }); +} + +loadPolyfills().then(main).catch(error => { + console.error(error); +}); diff --git a/app/javascript/packs/share.js b/app/javascript/packs/share.js new file mode 100644 index 000000000..e9580f648 --- /dev/null +++ b/app/javascript/packs/share.js @@ -0,0 +1,22 @@ +import loadPolyfills from '../mastodon/load_polyfills'; + +function loaded() { + const ComposeContainer = require('../mastodon/containers/compose_container').default; + const React = require('react'); + const ReactDOM = require('react-dom'); + const mountNode = document.getElementById('mastodon-compose'); + + if (mountNode !== null) { + const props = JSON.parse(mountNode.getAttribute('data-props')); + ReactDOM.render(, mountNode); + } +} + +function main() { + const ready = require('../mastodon/ready').default; + ready(loaded); +} + +loadPolyfills().then(main).catch(error => { + console.error(error); +}); diff --git a/app/javascript/styles/common.scss b/app/javascript/styles/common.scss deleted file mode 100644 index c1772e7ae..000000000 --- a/app/javascript/styles/common.scss +++ /dev/null @@ -1,5 +0,0 @@ -// This makes our fonts available everywhere. - -@import 'fonts/roboto'; -@import 'fonts/roboto-mono'; -@import 'fonts/montserrat'; diff --git a/app/javascript/styles/win95.scss b/app/javascript/styles/win95.scss index 885837b53..6c89fc5bf 100644 --- a/app/javascript/styles/win95.scss +++ b/app/javascript/styles/win95.scss @@ -1,3 +1,8 @@ +// win95 theme from cybrespace. + +// Modified to inherit glitch styles (themes/glitch/styles/index.scss) +// instead of vanilla ones (./application.scss) + $win95-bg: #bfbfbf; $win95-dark-grey: #404040; $win95-mid-grey: #808080; @@ -17,7 +22,7 @@ $ui-highlight-color: $win95-window-header; } @mixin win95-outset() { - box-shadow: inset -1px -1px 0px #000000, + box-shadow: inset -1px -1px 0px #000000, inset 1px 1px 0px #ffffff, inset -2px -2px 0px #808080, inset 2px 2px 0px #dfdfdf; @@ -41,7 +46,7 @@ $ui-highlight-color: $win95-window-header; } @mixin win95-inset() { - box-shadow: inset 1px 1px 0px #000000, + box-shadow: inset 1px 1px 0px #000000, inset -1px -1px 0px #ffffff, inset 2px 2px 0px #808080, inset -2px -2px 0px #dfdfdf; @@ -51,7 +56,7 @@ $ui-highlight-color: $win95-window-header; @mixin win95-tab() { - box-shadow: inset -1px 0px 0px #000000, + box-shadow: inset -1px 0px 0px #000000, inset 1px 0px 0px #ffffff, inset 0px 1px 0px #ffffff, inset 0px 2px 0px #dfdfdf, @@ -71,7 +76,7 @@ $ui-highlight-color: $win95-window-header; src: url('../fonts/premillenium/MSSansSerif.ttf') format('truetype'); } -@import 'application'; +@import '../themes/glitch/styles/index'; // Imports glitch themes /* borrowed from cybrespace style: wider columns and full column width images */ @@ -174,7 +179,7 @@ body.admin { font-size:0px; color:$win95-bg; - background-image: url("../images/start.png"); + background-image: url("../images/start.png"); background-repeat:no-repeat; background-position:8%; background-clip:padding-box; @@ -336,7 +341,7 @@ body.admin { border-radius:0px; background-color:white; @include win95-border-inset(); - + width:12px; height:12px; } @@ -515,9 +520,9 @@ body.admin { color:black; font-weight:bold; } -.account__avatar, -.account__avatar-overlay-base, -.account__header__avatar, +.account__avatar, +.account__avatar-overlay-base, +.account__header__avatar, .account__avatar-overlay-overlay { @include win95-border-slight-inset(); clip-path:none; @@ -627,7 +632,7 @@ body.admin { } .status-card__description { - color:black; + color:black; } .account__display-name strong, .status__display-name strong { @@ -710,8 +715,8 @@ body.admin { width:40px; font-size:0px; color:$win95-bg; - - background-image: url("../images/start.png"); + + background-image: url("../images/start.png"); background-repeat:no-repeat; background-position:8%; background-clip:padding-box; @@ -723,7 +728,7 @@ body.admin { } .drawer__header a:first-child:hover { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAOCAIAAACpTQvdAAAACXBIWXMAAC4jAAAuIwF4pT92AAAAF3pUWHRBdXRob3IAAAiZS84oys9LzAEAC5oC6A7BY/IAAACWSURBVCiRhVJJDsQgDEuqOfRZ7a1P5gbP4uaJaEjTADMWQhHYjlk4p0wLnNdptdF4KvBUDyGzVwc2xO+uKtH+1o0ytEEmqFpuxlvFCGCxKbNIT56QCi2MzaA/2Mz+mERSOeqzJG2RUxkjdTabgPtFoZ1bZxcKvgPcLZVufAyR9Ni8v5dWDzfFx0giC1RvZFv6l35QQ/Mvv39XXgGzQpoAAAAASUVORK5CYII="); + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAOCAIAAACpTQvdAAAACXBIWXMAAC4jAAAuIwF4pT92AAAAF3pUWHRBdXRob3IAAAiZS84oys9LzAEAC5oC6A7BY/IAAACWSURBVCiRhVJJDsQgDEuqOfRZ7a1P5gbP4uaJaEjTADMWQhHYjlk4p0wLnNdptdF4KvBUDyGzVwc2xO+uKtH+1o0ytEEmqFpuxlvFCGCxKbNIT56QCi2MzaA/2Mz+mERSOeqzJG2RUxkjdTabgPtFoZ1bZxcKvgPcLZVufAyR9Ni8v5dWDzfFx0giC1RvZFv6l35QQ/Mvv39XXgGzQpoAAAAASUVORK5CYII="); background-repeat:no-repeat; background-position:8%; background-clip:padding-box; @@ -732,7 +737,7 @@ body.admin { } .drawer__tab:first-child { - + } .search { @@ -844,7 +849,7 @@ body.admin { padding:4px 8px; } -.privacy-dropdown.active +.privacy-dropdown.active .privacy-dropdown__value { background: $win95-bg; box-shadow:unset; @@ -935,7 +940,7 @@ body.admin { background-color:$win95-bg; border:1px solid black; box-sizing:content-box; - + } .emoji-dialog .emoji-search { @@ -1010,8 +1015,8 @@ body.admin { width:60px; font-size:0px; color:$win95-bg; - - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAOCAIAAACpTQvdAAAACXBIWXMAAC4jAAAuIwF4pT92AAAAF3pUWHRBdXRob3IAAAiZS84oys9LzAEAC5oC6A7BY/IAAACWSURBVCiRhVJJDsQgDEuqOfRZ7a1P5gbP4uaJaEjTADMWQhHYjlk4p0wLnNdptdF4KvBUDyGzVwc2xO+uKtH+1o0ytEEmqFpuxlvFCGCxKbNIT56QCi2MzaA/2Mz+mERSOeqzJG2RUxkjdTabgPtFoZ1bZxcKvgPcLZVufAyR9Ni8v5dWDzfFx0giC1RvZFv6l35QQ/Mvv39XXgGzQpoAAAAASUVORK5CYII="); + + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAOCAIAAACpTQvdAAAACXBIWXMAAC4jAAAuIwF4pT92AAAAF3pUWHRBdXRob3IAAAiZS84oys9LzAEAC5oC6A7BY/IAAACWSURBVCiRhVJJDsQgDEuqOfRZ7a1P5gbP4uaJaEjTADMWQhHYjlk4p0wLnNdptdF4KvBUDyGzVwc2xO+uKtH+1o0ytEEmqFpuxlvFCGCxKbNIT56QCi2MzaA/2Mz+mERSOeqzJG2RUxkjdTabgPtFoZ1bZxcKvgPcLZVufAyR9Ni8v5dWDzfFx0giC1RvZFv6l35QQ/Mvv39XXgGzQpoAAAAASUVORK5CYII="); background-repeat:no-repeat; background-position:8%; background-clip:padding-box; @@ -1049,40 +1054,40 @@ body.admin { } } -.column-link[href="/web/timelines/public"] { - background-image: url("../images/icon_public.png"); +.column-link[href="/web/timelines/public"] { + background-image: url("../images/icon_public.png"); &:hover { background-image: url("../images/icon_public.png"); } } -.column-link[href="/web/timelines/public/local"] { - background-image: url("../images/icon_local.png"); +.column-link[href="/web/timelines/public/local"] { + background-image: url("../images/icon_local.png"); &:hover { background-image: url("../images/icon_local.png"); } } -.column-link[href="/web/pinned"] { - background-image: url("../images/icon_pin.png"); +.column-link[href="/web/pinned"] { + background-image: url("../images/icon_pin.png"); &:hover { background-image: url("../images/icon_pin.png"); } } -.column-link[href="/web/favourites"] { - background-image: url("../images/icon_likes.png"); +.column-link[href="/web/favourites"] { + background-image: url("../images/icon_likes.png"); &:hover { background-image: url("../images/icon_likes.png"); } } -.column-link[href="/web/blocks"] { - background-image: url("../images/icon_blocks.png"); +.column-link[href="/web/blocks"] { + background-image: url("../images/icon_blocks.png"); &:hover { background-image: url("../images/icon_blocks.png"); } } -.column-link[href="/web/mutes"] { - background-image: url("../images/icon_mutes.png"); +.column-link[href="/web/mutes"] { + background-image: url("../images/icon_mutes.png"); &:hover { background-image: url("../images/icon_mutes.png"); } } -.column-link[href="/settings/preferences"] { - background-image: url("../images/icon_settings.png"); +.column-link[href="/settings/preferences"] { + background-image: url("../images/icon_settings.png"); &:hover { background-image: url("../images/icon_settings.png"); } } -.column-link[href="/about/more"] { - background-image: url("../images/icon_about.png"); +.column-link[href="/about/more"] { + background-image: url("../images/icon_about.png"); &:hover { background-image: url("../images/icon_about.png"); } } -.column-link[href="/auth/sign_out"] { - background-image: url("../images/icon_logout.png"); +.column-link[href="/auth/sign_out"] { + background-image: url("../images/icon_logout.png"); &:hover { background-image: url("../images/icon_logout.png"); } } @@ -1098,7 +1103,7 @@ body.admin { line-height:30px; padding-left:20px; padding-right:40px; - + left:0px; bottom:-30px; display:block; @@ -1106,9 +1111,9 @@ body.admin { background-color:#7f7f7f; width:200%; height:30px; - + -ms-transform: rotate(-90deg); - + -webkit-transform: rotate(-90deg); transform: rotate(-90deg); transform-origin:top left; @@ -1189,7 +1194,7 @@ body.admin { left:unset; } -.dropdown > .icon-button, .detailed-status__button > .icon-button, +.dropdown > .icon-button, .detailed-status__button > .icon-button, .status__action-bar > .icon-button, .star-icon i { /* i don't know what's going on with the inline styles someone should look at the react code */ @@ -1239,8 +1244,8 @@ body.admin { background:$win95-bg; } -.actions-modal::before, -.boost-modal::before, +.actions-modal::before, +.boost-modal::before, .confirmation-modal::before, .report-modal::before { content: "Confirmation"; @@ -1278,8 +1283,8 @@ body.admin { .confirmation-modal__cancel-button { color:black; - &:active, - &:focus, + &:active, + &:focus, &:hover { color:black; } @@ -1566,10 +1571,10 @@ a.table-action-link:hover, background-color:white; } -.simple_form input[type=text], -.simple_form input[type=number], -.simple_form input[type=email], -.simple_form input[type=password], +.simple_form input[type=text], +.simple_form input[type=number], +.simple_form input[type=email], +.simple_form input[type=password], .simple_form textarea { color:black; background-color:white; @@ -1580,8 +1585,8 @@ a.table-action-link:hover, } } -.simple_form button, -.simple_form .button, +.simple_form button, +.simple_form .button, .simple_form .block-button { background: $win95-bg; @@ -1608,8 +1613,8 @@ a.table-action-link:hover, } } -.simple_form button.negative, -.simple_form .button.negative, +.simple_form button.negative, +.simple_form .button.negative, .simple_form .block-button.negative { background: $win95-bg; @@ -1631,8 +1636,8 @@ a.table-action-link:hover, border-right-color:#f5f5f5; width:12px; height:12px; - display:inline-block; - vertical-align:middle; + display:inline-block; + vertical-align:middle; margin-right:2px; } diff --git a/app/javascript/themes/glitch/containers/mastodon.js b/app/javascript/themes/glitch/containers/mastodon.js index 348470637..755b5564a 100644 --- a/app/javascript/themes/glitch/containers/mastodon.js +++ b/app/javascript/themes/glitch/containers/mastodon.js @@ -9,7 +9,7 @@ import UI from 'themes/glitch/features/ui'; import { hydrateStore } from 'themes/glitch/actions/store'; import { connectUserStream } from 'themes/glitch/actions/streaming'; import { IntlProvider, addLocaleData } from 'react-intl'; -import { getLocale } from 'mastodon/locales'; +import { getLocale } from 'locales'; import initialState from 'themes/glitch/util/initial_state'; const { localeData, messages } = getLocale(); diff --git a/app/javascript/themes/glitch/packs/common.js b/app/javascript/themes/glitch/packs/common.js index 3a62700bd..f4fa129e1 100644 --- a/app/javascript/themes/glitch/packs/common.js +++ b/app/javascript/themes/glitch/packs/common.js @@ -1,3 +1,3 @@ import 'font-awesome/css/font-awesome.css'; -require.context('../../images/', true); -import './styles/index.scss'; +require.context('images/', true); +import 'themes/glitch/styles/index.scss'; diff --git a/app/javascript/themes/glitch/packs/home.js b/app/javascript/themes/glitch/packs/home.js index dada28317..69dddf51c 100644 --- a/app/javascript/themes/glitch/packs/home.js +++ b/app/javascript/themes/glitch/packs/home.js @@ -1,7 +1,7 @@ -import loadPolyfills from './util/load_polyfills'; +import loadPolyfills from 'themes/glitch/util/load_polyfills'; loadPolyfills().then(() => { - require('./util/main').default(); + require('themes/glitch/util/main').default(); }).catch(e => { console.error(e); }); diff --git a/app/javascript/themes/glitch/packs/public.js b/app/javascript/themes/glitch/packs/public.js index 6adacad98..d9a1b9655 100644 --- a/app/javascript/themes/glitch/packs/public.js +++ b/app/javascript/themes/glitch/packs/public.js @@ -2,32 +2,14 @@ import loadPolyfills from 'themes/glitch/util/load_polyfills'; import { processBio } from 'themes/glitch/util/bio_metadata'; import ready from 'themes/glitch/util/ready'; -window.addEventListener('message', e => { - const data = e.data || {}; - - if (!window.parent || data.type !== 'setHeight') { - return; - } - - ready(() => { - window.parent.postMessage({ - type: 'setHeight', - id: data.id, - height: document.getElementsByTagName('html')[0].scrollHeight, - }, '*'); - }); -}); - function main() { - const { length } = require('stringz'); const IntlRelativeFormat = require('intl-relativeformat').default; - const { delegate } = require('rails-ujs'); - const emojify = require('../themes/glitch/util/emoji').default; - const { getLocale } = require('mastodon/locales'); + const emojify = require('themes/glitch/util/emoji').default; + const { getLocale } = require('locales'); const { localeData } = getLocale(); - const VideoContainer = require('../themes/glitch/containers/video_container').default; - const MediaGalleryContainer = require('../themes/glitch/containers/media_gallery_container').default; - const CardContainer = require('../themes/glitch/containers/card_container').default; + const VideoContainer = require('themes/glitch/containers/video_container').default; + const MediaGalleryContainer = require('themes/glitch/containers/media_gallery_container').default; + const CardContainer = require('themes/glitch/containers/card_container').default; const React = require('react'); const ReactDOM = require('react-dom'); @@ -87,61 +69,6 @@ function main() { ReactDOM.render(, content); }); }); - - delegate(document, '.webapp-btn', 'click', ({ target, button }) => { - if (button !== 0) { - return true; - } - window.location.href = target.href; - return false; - }); - - delegate(document, '.status__content__spoiler-link', 'click', ({ target }) => { - const contentEl = target.parentNode.parentNode.querySelector('.e-content'); - - if (contentEl.style.display === 'block') { - contentEl.style.display = 'none'; - target.parentNode.style.marginBottom = 0; - } else { - contentEl.style.display = 'block'; - target.parentNode.style.marginBottom = null; - } - - return false; - }); - - delegate(document, '.account_display_name', 'input', ({ target }) => { - const nameCounter = document.querySelector('.name-counter'); - - if (nameCounter) { - nameCounter.textContent = 30 - length(target.value); - } - }); - - delegate(document, '.account_note', 'input', ({ target }) => { - const noteCounter = document.querySelector('.note-counter'); - - if (noteCounter) { - const noteWithoutMetadata = processBio(target.value).text; - noteCounter.textContent = 500 - length(noteWithoutMetadata); - } - }); - - delegate(document, '#account_avatar', 'change', ({ target }) => { - const avatar = document.querySelector('.card.compact .avatar img'); - const [file] = target.files || []; - const url = file ? URL.createObjectURL(file) : avatar.dataset.originalSrc; - - avatar.src = url; - }); - - delegate(document, '#account_header', 'change', ({ target }) => { - const header = document.querySelector('.card.compact'); - const [file] = target.files || []; - const url = file ? URL.createObjectURL(file) : header.dataset.originalSrc; - - header.style.backgroundImage = `url(${url})`; - }); } loadPolyfills().then(main).catch(error => { diff --git a/app/javascript/themes/glitch/theme.yml b/app/javascript/themes/glitch/theme.yml index cf3fa32c2..ac6f57546 100644 --- a/app/javascript/themes/glitch/theme.yml +++ b/app/javascript/themes/glitch/theme.yml @@ -1,25 +1,34 @@ # (REQUIRED) The location of the pack files. pack: about: packs/about.js - admin: null - common: packs/common.js - embed: null - home: packs/home.js + admin: + auth: + common: + filename: packs/common.js + stylesheet: true + embed: packs/public.js + error: + home: + filename: packs/home.js + preload: + - themes/glitch/async/getting_started + - themes/glitch/async/compose + - themes/glitch/async/home_timeline + - themes/glitch/async/notifications + stylesheet: true + modal: public: packs/public.js - settings: null + settings: share: packs/share.js # (OPTIONAL) The directory which contains the pack files. # Defaults to the theme directory (`app/javascript/themes/[theme]`), # which should be sufficient for like 99% of use-cases lol. -# pack_directory: app/javascript/packs -# (OPTIONAL) Additional javascript resources to preload, for use with -# lazy-loaded components. It is **STRONGLY RECOMMENDED** that you -# derive these pathnames from `themes/[your-theme]` to ensure that -# they stay unique. -preload: -- themes/glitch/async/getting_started -- themes/glitch/async/compose -- themes/glitch/async/home_timeline -- themes/glitch/async/notifications +# pack_directory: app/javascript/packs + +# (OPTIONAL) By default the theme will fallback to the default theme +# if a particular pack is not provided. You can specify different +# fallbacks here, or disable fallback behaviours altogether by +# specifying a `null` value. +fallback: diff --git a/app/javascript/themes/vanilla/theme.yml b/app/javascript/themes/vanilla/theme.yml index b4a1598fc..67fd9723e 100644 --- a/app/javascript/themes/vanilla/theme.yml +++ b/app/javascript/themes/vanilla/theme.yml @@ -1,12 +1,23 @@ # (REQUIRED) The location of the pack files inside `pack_directory`. pack: about: about.js - admin: null - common: common.js - embed: null - home: application.js + admin: + auth: + common: + filename: common.js + stylesheet: true + embed: public.js + error: + home: + filename: application.js + preload: + - features/getting_started + - features/compose + - features/home_timeline + - features/notifications + modal: public: public.js - settings: null + settings: share: share.js # (OPTIONAL) The directory which contains the pack files. @@ -15,12 +26,8 @@ pack: # somewhere else. pack_directory: app/javascript/packs -# (OPTIONAL) Additional javascript resources to preload, for use with -# lazy-loaded components. It is **STRONGLY RECOMMENDED** that you -# derive these pathnames from `themes/[your-theme]` to ensure that -# they stay unique. (Of course, vanilla doesn't do this ^^;;) -preload: -- features/getting_started -- features/compose -- features/home_timeline -- features/notifications +# (OPTIONAL) By default the theme will fallback to the default theme +# if a particular pack is not provided. You can specify different +# fallbacks here, or disable fallback behaviours altogether by +# specifying a `null` value. +fallback: diff --git a/app/javascript/themes/win95/index.js b/app/javascript/themes/win95/index.js new file mode 100644 index 000000000..bed6a1ef3 --- /dev/null +++ b/app/javascript/themes/win95/index.js @@ -0,0 +1,10 @@ +// These lines are the same as in glitch: +import 'font-awesome/css/font-awesome.css'; +require.context('../../images/', true); + +// …But we want to use our own styles instead. +import 'styles/win95.scss'; + +// Be sure to make this style file import from +// `themes/glitch/styles/index.scss` (the glitch styling), and not +// `application.scss` (which are the vanilla styles). diff --git a/app/javascript/themes/win95/theme.yml b/app/javascript/themes/win95/theme.yml new file mode 100644 index 000000000..43af38198 --- /dev/null +++ b/app/javascript/themes/win95/theme.yml @@ -0,0 +1,23 @@ +# win95 theme. + +# Ported over from `cybrespace:mastodon/theme_win95`. +# + +# You can use this theme file as inspiration for porting over +# a preëxisting Mastodon theme. + +# We only modify the `common` pack, which contains our styling. +pack: + common: + filename: index.js + stylesheet: true + # All unspecified packs will inherit from glitch. + +# By default, the glitch preloads will also be used here. You can +# disable them by setting `preload` to `null`. + +# preload: + +# The `fallback` parameter tells us to use glitch files for everything +# we haven't specified. +fallback: glitch diff --git a/app/lib/themes.rb b/app/lib/themes.rb index f7ec22fd2..7ced9f945 100644 --- a/app/lib/themes.rb +++ b/app/lib/themes.rb @@ -7,15 +7,27 @@ class Themes include Singleton def initialize + + core = YAML.load_file(Rails.root.join('app', 'javascript', 'core', 'theme.yml')) + core['pack'] = Hash.new unless core['pack'] + result = Hash.new Dir.glob(Rails.root.join('app', 'javascript', 'themes', '*', 'theme.yml')) do |path| data = YAML.load_file(path) name = File.basename(File.dirname(path)) if data['pack'] + data['name'] = name result[name] = data end end + + @core = core @conf = result + + end + + def core + @core end def get(name) diff --git a/app/views/about/more.html.haml b/app/views/about/more.html.haml index 7ffa5ecc3..d92362bd7 100644 --- a/app/views/about/more.html.haml +++ b/app/views/about/more.html.haml @@ -2,7 +2,6 @@ = site_hostname - content_for :header_tags do - = javascript_pack_tag 'public', integrity: true, crossorigin: 'anonymous' = render partial: 'shared/og' .landing-page diff --git a/app/views/about/show.html.haml b/app/views/about/show.html.haml index 385b0b1dc..4f5b53470 100644 --- a/app/views/about/show.html.haml +++ b/app/views/about/show.html.haml @@ -3,7 +3,6 @@ - content_for :header_tags do %script#initial-state{ type: 'application/json' }!= json_escape(@initial_state_json) - = javascript_pack_tag 'about', integrity: true, crossorigin: 'anonymous' = render partial: 'shared/og' .landing-page diff --git a/app/views/admin/reports/show.html.haml b/app/views/admin/reports/show.html.haml index 5747cc274..7dd962bf2 100644 --- a/app/views/admin/reports/show.html.haml +++ b/app/views/admin/reports/show.html.haml @@ -1,6 +1,3 @@ -- content_for :header_tags do - = javascript_pack_tag 'admin', integrity: true, async: true, crossorigin: 'anonymous' - - content_for :page_title do = t('admin.reports.report', id: @report.id) diff --git a/app/views/admin/statuses/index.html.haml b/app/views/admin/statuses/index.html.haml index fe2581527..9747a92cf 100644 --- a/app/views/admin/statuses/index.html.haml +++ b/app/views/admin/statuses/index.html.haml @@ -1,6 +1,3 @@ -- content_for :header_tags do - = javascript_pack_tag 'admin', integrity: true, async: true, crossorigin: 'anonymous' - - content_for :page_title do = t('admin.statuses.title') diff --git a/app/views/home/index.html.haml b/app/views/home/index.html.haml index 63b3a0c26..e8a81656c 100644 --- a/app/views/home/index.html.haml +++ b/app/views/home/index.html.haml @@ -1,13 +1,7 @@ - content_for :header_tags do - - if theme_data['preload'] - - theme_data['preload'].each do |link| - %link{ href: asset_pack_path("#{link}.js"), crossorigin: 'anonymous', rel: 'preload', as: 'script' }/ %meta{name: 'applicationServerKey', content: Rails.configuration.x.vapid_public_key} %script#initial-state{ type: 'application/json' }!= json_escape(@initial_state_json) - = javascript_pack_tag "themes/#{current_theme}", integrity: true, crossorigin: 'anonymous' - = stylesheet_pack_tag "themes/#{current_theme}", integrity: true, media: 'all' - .app-holder#mastodon{ data: { props: Oj.dump(default_props) } } %noscript = image_tag asset_pack_path('logo.svg'), alt: 'Mastodon' diff --git a/app/views/layouts/_theme.html.haml b/app/views/layouts/_theme.html.haml new file mode 100644 index 000000000..cdec4b370 --- /dev/null +++ b/app/views/layouts/_theme.html.haml @@ -0,0 +1,10 @@ +- if theme + - if theme[:pack] != 'common' && theme[:common] + = render partial: 'layouts/theme', object: theme[:common] + - if theme[:pack] + = javascript_pack_tag theme[:name] ? "themes/#{theme[:name]}/#{theme[:pack]}" : "core/#{theme[:pack]}", integrity: true, crossorigin: 'anonymous' + - if theme[:stylesheet] + = stylesheet_pack_tag theme[:name] ? "themes/#{theme[:name]}/#{theme[:pack]}" : "core/#{theme[:pack]}", integrity: true, media: 'all' + - if theme[:preload] + - theme[:preload].each do |link| + %link{ href: asset_pack_path("#{link}.js"), crossorigin: 'anonymous', rel: 'preload', as: 'script' }/ diff --git a/app/views/layouts/admin.html.haml b/app/views/layouts/admin.html.haml index c98d85f7b..66382db50 100644 --- a/app/views/layouts/admin.html.haml +++ b/app/views/layouts/admin.html.haml @@ -1,6 +1,3 @@ -- content_for :header_tags do - = javascript_pack_tag 'public', integrity: true, crossorigin: 'anonymous' - - content_for :content do .admin-wrapper .sidebar-wrapper diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index 24b74c787..99ae7d90d 100755 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -18,16 +18,16 @@ = ' - ' = title - = stylesheet_pack_tag 'common', media: 'all' - = javascript_pack_tag 'common', integrity: true, crossorigin: 'anonymous' + = javascript_pack_tag "locales", integrity: true, crossorigin: 'anonymous' = javascript_pack_tag "locale_#{I18n.locale}", integrity: true, crossorigin: 'anonymous' = csrf_meta_tags - - if controller_name != 'home' - = stylesheet_pack_tag 'application', integrity: true, media: 'all' - = yield :header_tags + -# These must come after :header_tags to ensure our initial state has been defined. + = render partial: 'layouts/theme', object: @core + = render partial: 'layouts/theme', object: @theme + - body_classes ||= @body_classes || '' - body_classes += ' system-font' if current_account&.user&.setting_system_font_ui diff --git a/app/views/layouts/auth.html.haml b/app/views/layouts/auth.html.haml index d8ac733f9..f4812ac6a 100644 --- a/app/views/layouts/auth.html.haml +++ b/app/views/layouts/auth.html.haml @@ -1,6 +1,3 @@ -- content_for :header_tags do - = javascript_pack_tag 'public', integrity: true, crossorigin: 'anonymous' - - content_for :content do .container .logo-container diff --git a/app/views/layouts/embedded.html.haml b/app/views/layouts/embedded.html.haml index 5fc60be17..3960167bf 100644 --- a/app/views/layouts/embedded.html.haml +++ b/app/views/layouts/embedded.html.haml @@ -4,10 +4,9 @@ %meta{ charset: 'utf-8' }/ %meta{ name: 'robots', content: 'noindex' }/ - = stylesheet_pack_tag 'common', media: 'all' = javascript_pack_tag 'common', integrity: true, crossorigin: 'anonymous' - = stylesheet_pack_tag 'application', integrity: true, media: 'all' + = javascript_pack_tag 'embed', integrity: true, crossorigin: 'anonymous' = javascript_pack_tag "locale_#{I18n.locale}", integrity: true, crossorigin: 'anonymous' - = javascript_pack_tag 'public', integrity: true, crossorigin: 'anonymous' - %body.embed + = render partial: 'layouts/theme', object: @core + = render partial: 'layouts/theme', object: @theme = yield diff --git a/app/views/layouts/error.html.haml b/app/views/layouts/error.html.haml index d0eae4434..9904b8fdd 100644 --- a/app/views/layouts/error.html.haml +++ b/app/views/layouts/error.html.haml @@ -5,8 +5,8 @@ %meta{ charset: 'utf-8' }/ %title= safe_join([yield(:page_title), Setting.default_settings['site_title']], ' - ') %meta{ content: 'width=device-width,initial-scale=1', name: 'viewport' }/ - = stylesheet_pack_tag 'common', media: 'all' - = stylesheet_pack_tag 'application', integrity: true, media: 'all' + = render partial: 'layouts/theme', object: @core + = render partial: 'layouts/theme', object: @theme %body.error .dialog %img{ alt: Setting.default_settings['site_title'], src: '/oops.gif' }/ diff --git a/app/views/layouts/modal.html.haml b/app/views/layouts/modal.html.haml index a819e098d..d3519f032 100644 --- a/app/views/layouts/modal.html.haml +++ b/app/views/layouts/modal.html.haml @@ -1,6 +1,3 @@ -- content_for :header_tags do - = javascript_pack_tag 'public', integrity: true, crossorigin: 'anonymous' - - content_for :content do - if user_signed_in? .account-header diff --git a/app/views/layouts/public.html.haml b/app/views/layouts/public.html.haml index 83e92b938..b3795eaad 100644 --- a/app/views/layouts/public.html.haml +++ b/app/views/layouts/public.html.haml @@ -1,6 +1,3 @@ -- content_for :header_tags do - = javascript_pack_tag 'public', integrity: true, crossorigin: 'anonymous' - - content_for :content do .container= yield .footer diff --git a/app/views/shares/show.html.haml b/app/views/shares/show.html.haml index 44b6f145f..4c0390c42 100644 --- a/app/views/shares/show.html.haml +++ b/app/views/shares/show.html.haml @@ -1,5 +1,4 @@ - content_for :header_tags do %script#initial-state{ type: 'application/json' }!= json_escape(@initial_state_json) - = javascript_pack_tag 'share', integrity: true, crossorigin: 'anonymous' #mastodon-compose{ data: { props: Oj.dump(default_props) } } diff --git a/app/views/tags/show.html.haml b/app/views/tags/show.html.haml index ea8b0faa3..e05fe1c39 100644 --- a/app/views/tags/show.html.haml +++ b/app/views/tags/show.html.haml @@ -3,7 +3,6 @@ - content_for :header_tags do %script#initial-state{ type: 'application/json' }!= json_escape(@initial_state_json) - = javascript_pack_tag 'about', integrity: true, crossorigin: 'anonymous' = render 'og' .landing-page.tag-page diff --git a/config/webpack/configuration.js b/config/webpack/configuration.js index 74f75d89b..9514bc547 100644 --- a/config/webpack/configuration.js +++ b/config/webpack/configuration.js @@ -12,14 +12,24 @@ const settings = safeLoad(readFileSync(configPath), 'utf8')[env.NODE_ENV]; const themeFiles = glob.sync('app/javascript/themes/*/theme.yml'); const themes = {}; +const core = function () { + const coreFile = resolve('app', 'javascript', 'core', 'theme.yml'); + const data = safeLoad(readFileSync(coreFile), 'utf8'); + if (!data.pack_directory) { + data.pack_directory = dirname(coreFile); + } + return data.pack ? data : {}; +}(); + for (let i = 0; i < themeFiles.length; i++) { const themeFile = themeFiles[i]; const data = safeLoad(readFileSync(themeFile), 'utf8'); + data.name = basename(dirname(themeFile)); if (!data.pack_directory) { data.pack_directory = dirname(themeFile); } if (data.pack) { - themes[basename(dirname(themeFile))] = data; + themes[data.name] = data; } } @@ -43,6 +53,7 @@ const output = { module.exports = { settings, + core, themes, env, loadersDir, diff --git a/config/webpack/generateLocalePacks.js b/config/webpack/generateLocalePacks.js index cd3bed50c..a943589f7 100644 --- a/config/webpack/generateLocalePacks.js +++ b/config/webpack/generateLocalePacks.js @@ -57,7 +57,7 @@ Object.keys(glitchMessages).forEach(function (key) { // import messages from '../../app/javascript/mastodon/locales/${locale}.json'; import localeData from ${JSON.stringify(localeDataPath)}; -import { setLocale } from '../../app/javascript/mastodon/locales'; +import { setLocale } from 'locales'; ${glitchInject} setLocale({messages: mergedMessages, localeData: localeData}); `; diff --git a/config/webpack/shared.js b/config/webpack/shared.js index 5d176db4e..5b90f27fb 100644 --- a/config/webpack/shared.js +++ b/config/webpack/shared.js @@ -6,33 +6,37 @@ const { sync } = require('glob'); const ExtractTextPlugin = require('extract-text-webpack-plugin'); const ManifestPlugin = require('webpack-manifest-plugin'); const extname = require('path-complete-extname'); -const { env, settings, themes, output, loadersDir } = require('./configuration.js'); +const { env, settings, core, themes, output, loadersDir } = require('./configuration.js'); const localePackPaths = require('./generateLocalePacks'); -const extensionGlob = `**/*{${settings.extensions.join(',')}}*`; -const entryPath = join(settings.source_path, settings.source_entry_path); -const packPaths = sync(join(entryPath, extensionGlob)); +function reducePacks (data, into = {}) { + if (!data.pack) { + return into; + } + Object.keys(data.pack).reduce((map, entry) => { + const pack = data.pack[entry]; + if (!pack) { + return map; + } + const packFile = typeof pack === 'string' ? pack : pack.filename; + if (packFile) { + map[data.name ? `themes/${data.name}/${entry}` : `core/${entry}`] = resolve(data.pack_directory, packFile); + } + return map; + }, into); + return into; +} module.exports = { entry: Object.assign( - packPaths.reduce((map, entry) => { - const localMap = map; - const namespace = relative(join(entryPath), dirname(entry)); - localMap[join(namespace, basename(entry, extname(entry)))] = resolve(entry); - return localMap; - }, {}), + { locales: resolve('app', 'javascript', 'locales') }, localePackPaths.reduce((map, entry) => { const localMap = map; localMap[basename(entry, extname(entry, extname(entry)))] = resolve(entry); return localMap; }, {}), - Object.keys(themes).reduce( - (themePaths, name) => { - const themeData = themes[name]; - themePaths[`themes/${name}`] = resolve(themeData.pack_directory, themeData.pack); - return themePaths; - }, {} - ) + reducePacks(core), + Object.keys(themes).reduce((map, entry) => reducePacks(themes[entry], map), {}) ), output: { @@ -64,7 +68,7 @@ module.exports = { writeToFileEmit: true, }), new webpack.optimize.CommonsChunkPlugin({ - name: 'common', + name: 'locales', minChunks: Infinity, // It doesn't make sense to use common chunks with multiple frontend support. }), ], diff --git a/config/webpacker.yml b/config/webpacker.yml index 8d8470651..50d95813a 100644 --- a/config/webpacker.yml +++ b/config/webpacker.yml @@ -2,7 +2,6 @@ default: &default source_path: app/javascript - source_entry_path: packs public_output_path: packs cache_path: tmp/cache/webpacker @@ -13,17 +12,6 @@ default: &default # Reload manifest.json on all requests so we reload latest compiled packs cache_manifest: false - extensions: - - .js - - .sass - - .scss - - .css - - .png - - .svg - - .gif - - .jpeg - - .jpg - development: <<: *default compile: true -- cgit From 541fe9b110fce15c42ba15df27926552c234afd0 Mon Sep 17 00:00:00 2001 From: kibigo! Date: Thu, 30 Nov 2017 19:29:47 -0800 Subject: Skins support --- app/controllers/application_controller.rb | 43 ++++++++++++------- app/controllers/settings/preferences_controller.rb | 1 + app/javascript/core/common.js | 3 ++ app/javascript/core/settings.js | 6 +++ app/javascript/core/theme.yml | 4 +- app/javascript/packs/common.js | 4 +- app/javascript/skins/vanilla/win95.scss | 1 + app/javascript/styles/mastodon/components.scss | 8 ++-- app/javascript/styles/win95.scss | 48 +++++++++++----------- app/javascript/themes/glitch/packs/common.js | 2 - app/javascript/themes/glitch/packs/public.js | 1 - app/javascript/themes/win95/index.js | 10 ----- app/javascript/themes/win95/theme.yml | 18 -------- app/lib/themes.rb | 25 +++++++++++ app/lib/user_settings_decorator.rb | 7 +++- app/models/user.rb | 2 +- app/views/layouts/_theme.html.haml | 7 +++- app/views/settings/preferences/show.html.haml | 1 + config/locales/simple_form.en.yml | 2 + config/settings.yml | 1 + config/webpack/configuration.js | 28 +++++++++++-- config/webpack/shared.js | 20 ++++++++- 22 files changed, 157 insertions(+), 85 deletions(-) create mode 100644 app/javascript/skins/vanilla/win95.scss delete mode 100644 app/javascript/themes/win95/index.js delete mode 100644 app/javascript/themes/win95/theme.yml (limited to 'app/javascript/styles') diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 7cc4eea27..f5753963d 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -12,6 +12,8 @@ class ApplicationController < ActionController::Base helper_method :current_account helper_method :current_session + helper_method :current_theme + helper_method :current_skin helper_method :single_user_mode? rescue_from ActionController::RoutingError, with: :not_found @@ -52,14 +54,14 @@ class ApplicationController < ActionController::Base new_user_session_path end - def pack(data, pack_name) + def pack(data, pack_name, skin = 'default') return nil unless pack?(data, pack_name) pack_data = { common: pack_name == 'common' ? nil : resolve_pack(data['name'] ? Themes.instance.get(current_theme) : Themes.instance.core, 'common'), name: data['name'], pack: pack_name, preload: nil, - stylesheet: false + skin: nil, } if data['pack'][pack_name].is_a?(Hash) pack_data[:common] = nil if data['pack'][pack_name]['use_common'] == false @@ -68,7 +70,11 @@ class ApplicationController < ActionController::Base pack_data[:preload] = [data['pack'][pack_name]['preload']] if data['pack'][pack_name]['preload'].is_a?(String) pack_data[:preload] = data['pack'][pack_name]['preload'] if data['pack'][pack_name]['preload'].is_a?(Array) end - pack_data[:stylesheet] = true if data['pack'][pack_name]['stylesheet'] + if skin != 'default' && data['skin'][skin] + pack_data[:skin] = skin if data['skin'][skin].include?(pack_name) + else # default skin + pack_data[:skin] = 'default' if data['pack'][pack_name]['stylesheet'] + end end pack_data end @@ -80,39 +86,39 @@ class ApplicationController < ActionController::Base false end - def nil_pack(data, pack_name) + def nil_pack(data, pack_name, skin = 'default') { - common: pack_name == 'common' ? nil : resolve_pack(data['name'] ? Themes.instance.get(current_theme) : Themes.instance.core, 'common'), + common: pack_name == 'common' ? nil : resolve_pack(data['name'] ? Themes.instance.get(current_theme) : Themes.instance.core, 'common', skin), name: data['name'], pack: nil, preload: nil, - stylesheet: false + skin: nil, } end - def resolve_pack(data, pack_name) - result = pack(data, pack_name) + def resolve_pack(data, pack_name, skin = 'default') + result = pack(data, pack_name, skin) unless result if data['name'] && data.key?('fallback') if data['fallback'].nil? - return nil_pack(data, pack_name) + return nil_pack(data, pack_name, skin) elsif data['fallback'].is_a?(String) && Themes.instance.get(data['fallback']) - return resolve_pack(Themes.instance.get(data['fallback']), pack_name) + return resolve_pack(Themes.instance.get(data['fallback']), pack_name, skin) elsif data['fallback'].is_a?(Array) data['fallback'].each do |fallback| - return resolve_pack(Themes.instance.get(fallback), pack_name) if Themes.instance.get(fallback) + return resolve_pack(Themes.instance.get(fallback), pack_name, skin) if Themes.instance.get(fallback) end end - return nil_pack(data, pack_name) + return nil_pack(data, pack_name, skin) end - return data.key?('name') && data['name'] != default_theme ? resolve_pack(Themes.instance.get(default_theme), pack_name) : nil_pack(data, pack_name) + return data.key?('name') && data['name'] != default_theme ? resolve_pack(Themes.instance.get(default_theme), pack_name, skin) : nil_pack(data, pack_name, skin) end result end def use_pack(pack_name) @core = resolve_pack(Themes.instance.core, pack_name) - @theme = resolve_pack(Themes.instance.get(current_theme), pack_name) + @theme = resolve_pack(Themes.instance.get(current_theme), pack_name, current_skin) end protected @@ -154,6 +160,15 @@ class ApplicationController < ActionController::Base current_user.setting_theme end + def default_skin + 'default' + end + + def current_skin + return default_skin unless Themes.instance.skins_for(current_theme).include? current_user&.setting_skin + current_user.setting_skin + end + def cache_collection(raw, klass) return raw unless klass.respond_to?(:with_includes) diff --git a/app/controllers/settings/preferences_controller.rb b/app/controllers/settings/preferences_controller.rb index 3aefd90a2..56baebed2 100644 --- a/app/controllers/settings/preferences_controller.rb +++ b/app/controllers/settings/preferences_controller.rb @@ -39,6 +39,7 @@ class Settings::PreferencesController < Settings::BaseController :setting_system_font_ui, :setting_noindex, :setting_theme, + :setting_skin, notification_emails: %i(follow follow_request reblog favourite mention digest), interactions: %i(must_be_follower must_be_following) ) diff --git a/app/javascript/core/common.js b/app/javascript/core/common.js index 24c0fdf61..bb4b97935 100644 --- a/app/javascript/core/common.js +++ b/app/javascript/core/common.js @@ -1,5 +1,8 @@ // This file will be loaded on all pages, regardless of theme. import { start } from 'rails-ujs'; +import 'font-awesome/css/font-awesome.css'; + +require.context('images/', true); start(); diff --git a/app/javascript/core/settings.js b/app/javascript/core/settings.js index 7fb1d8e77..bc5f9ed1d 100644 --- a/app/javascript/core/settings.js +++ b/app/javascript/core/settings.js @@ -3,6 +3,8 @@ const { length } = require('stringz'); const { delegate } = require('rails-ujs'); +import { processBio } from 'themes/glitch/util/bio_metadata'; + delegate(document, '.account_display_name', 'input', ({ target }) => { const nameCounter = document.querySelector('.name-counter'); @@ -35,3 +37,7 @@ delegate(document, '#account_header', 'change', ({ target }) => { header.style.backgroundImage = `url(${url})`; }); + +delegate(document, '#user_setting_theme', 'change', ({ target }) => { + target.form.submit(); +}); diff --git a/app/javascript/core/theme.yml b/app/javascript/core/theme.yml index 17e8e66b3..0dc05a149 100644 --- a/app/javascript/core/theme.yml +++ b/app/javascript/core/theme.yml @@ -4,7 +4,9 @@ pack: about: admin: admin.js auth: - common: common.js + common: + filename: common.js + stylesheet: true embed: embed.js error: home: diff --git a/app/javascript/packs/common.js b/app/javascript/packs/common.js index f3156c1c6..5d42509c5 100644 --- a/app/javascript/packs/common.js +++ b/app/javascript/packs/common.js @@ -1,3 +1 @@ -import 'font-awesome/css/font-awesome.css'; -import 'styles/application.scss' -require.context('../images/', true); +import 'styles/application.scss'; diff --git a/app/javascript/skins/vanilla/win95.scss b/app/javascript/skins/vanilla/win95.scss new file mode 100644 index 000000000..298f6ee9d --- /dev/null +++ b/app/javascript/skins/vanilla/win95.scss @@ -0,0 +1 @@ +@import 'styles/win95'; diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index 0ded6f159..8566585c5 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -2075,7 +2075,7 @@ .getting-started { box-sizing: border-box; padding-bottom: 235px; - background: url('../images/mastodon-getting-started.png') no-repeat 0 100%; + background: url('~images/mastodon-getting-started.png') no-repeat 0 100%; flex: 1 0 auto; p { @@ -2270,7 +2270,7 @@ button.icon-button.active i.fa-retweet { justify-content: center; & > div { - background: url('../images/mastodon-not-found.png') no-repeat center -50px; + background: url('~images/mastodon-not-found.png') no-repeat center -50px; padding-top: 210px; width: 100%; } @@ -3143,7 +3143,7 @@ button.icon-button.active i.fa-retweet { img, canvas { display: block; - background: url('../images/void.png') repeat; + background: url('~images/void.png') repeat; object-fit: contain; } @@ -3390,7 +3390,7 @@ button.icon-button.active i.fa-retweet { } .onboarding-modal__page-one__elephant-friend { - background: url('../images/elephant-friend-1.png') no-repeat center center / contain; + background: url('~images/elephant-friend-1.png') no-repeat center center / contain; width: 155px; height: 193px; margin-right: 15px; diff --git a/app/javascript/styles/win95.scss b/app/javascript/styles/win95.scss index 6c89fc5bf..d683218b0 100644 --- a/app/javascript/styles/win95.scss +++ b/app/javascript/styles/win95.scss @@ -1,7 +1,7 @@ // win95 theme from cybrespace. -// Modified to inherit glitch styles (themes/glitch/styles/index.scss) -// instead of vanilla ones (./application.scss) +// Modified by kibi! to use webpack package syntax for urls (eg, +// `url(~images/…)`) for easy importing into skins. $win95-bg: #bfbfbf; $win95-dark-grey: #404040; @@ -73,10 +73,10 @@ $ui-highlight-color: $win95-window-header; @font-face { font-family:"premillenium"; - src: url('../fonts/premillenium/MSSansSerif.ttf') format('truetype'); + src: url('~fonts/premillenium/MSSansSerif.ttf') format('truetype'); } -@import '../themes/glitch/styles/index'; // Imports glitch themes +@import 'application'; /* borrowed from cybrespace style: wider columns and full column width images */ @@ -179,7 +179,7 @@ body.admin { font-size:0px; color:$win95-bg; - background-image: url("../images/start.png"); + background-image: url("~images/start.png"); background-repeat:no-repeat; background-position:8%; background-clip:padding-box; @@ -716,7 +716,7 @@ body.admin { font-size:0px; color:$win95-bg; - background-image: url("../images/start.png"); + background-image: url("~images/start.png"); background-repeat:no-repeat; background-position:8%; background-clip:padding-box; @@ -1055,40 +1055,40 @@ body.admin { } .column-link[href="/web/timelines/public"] { - background-image: url("../images/icon_public.png"); - &:hover { background-image: url("../images/icon_public.png"); } + background-image: url("~images/icon_public.png"); + &:hover { background-image: url("~images/icon_public.png"); } } .column-link[href="/web/timelines/public/local"] { - background-image: url("../images/icon_local.png"); - &:hover { background-image: url("../images/icon_local.png"); } + background-image: url("~images/icon_local.png"); + &:hover { background-image: url("~images/icon_local.png"); } } .column-link[href="/web/pinned"] { - background-image: url("../images/icon_pin.png"); - &:hover { background-image: url("../images/icon_pin.png"); } + background-image: url("~images/icon_pin.png"); + &:hover { background-image: url("~images/icon_pin.png"); } } .column-link[href="/web/favourites"] { - background-image: url("../images/icon_likes.png"); - &:hover { background-image: url("../images/icon_likes.png"); } + background-image: url("~images/icon_likes.png"); + &:hover { background-image: url("~images/icon_likes.png"); } } .column-link[href="/web/blocks"] { - background-image: url("../images/icon_blocks.png"); - &:hover { background-image: url("../images/icon_blocks.png"); } + background-image: url("~images/icon_blocks.png"); + &:hover { background-image: url("~images/icon_blocks.png"); } } .column-link[href="/web/mutes"] { - background-image: url("../images/icon_mutes.png"); - &:hover { background-image: url("../images/icon_mutes.png"); } + background-image: url("~images/icon_mutes.png"); + &:hover { background-image: url("~images/icon_mutes.png"); } } .column-link[href="/settings/preferences"] { - background-image: url("../images/icon_settings.png"); - &:hover { background-image: url("../images/icon_settings.png"); } + background-image: url("~images/icon_settings.png"); + &:hover { background-image: url("~images/icon_settings.png"); } } .column-link[href="/about/more"] { - background-image: url("../images/icon_about.png"); - &:hover { background-image: url("../images/icon_about.png"); } + background-image: url("~images/icon_about.png"); + &:hover { background-image: url("~images/icon_about.png"); } } .column-link[href="/auth/sign_out"] { - background-image: url("../images/icon_logout.png"); - &:hover { background-image: url("../images/icon_logout.png"); } + background-image: url("~images/icon_logout.png"); + &:hover { background-image: url("~images/icon_logout.png"); } } .getting-started__footer { diff --git a/app/javascript/themes/glitch/packs/common.js b/app/javascript/themes/glitch/packs/common.js index f4fa129e1..fd4254a0e 100644 --- a/app/javascript/themes/glitch/packs/common.js +++ b/app/javascript/themes/glitch/packs/common.js @@ -1,3 +1 @@ -import 'font-awesome/css/font-awesome.css'; -require.context('images/', true); import 'themes/glitch/styles/index.scss'; diff --git a/app/javascript/themes/glitch/packs/public.js b/app/javascript/themes/glitch/packs/public.js index d9a1b9655..410e66716 100644 --- a/app/javascript/themes/glitch/packs/public.js +++ b/app/javascript/themes/glitch/packs/public.js @@ -1,5 +1,4 @@ import loadPolyfills from 'themes/glitch/util/load_polyfills'; -import { processBio } from 'themes/glitch/util/bio_metadata'; import ready from 'themes/glitch/util/ready'; function main() { diff --git a/app/javascript/themes/win95/index.js b/app/javascript/themes/win95/index.js deleted file mode 100644 index bed6a1ef3..000000000 --- a/app/javascript/themes/win95/index.js +++ /dev/null @@ -1,10 +0,0 @@ -// These lines are the same as in glitch: -import 'font-awesome/css/font-awesome.css'; -require.context('../../images/', true); - -// …But we want to use our own styles instead. -import 'styles/win95.scss'; - -// Be sure to make this style file import from -// `themes/glitch/styles/index.scss` (the glitch styling), and not -// `application.scss` (which are the vanilla styles). diff --git a/app/javascript/themes/win95/theme.yml b/app/javascript/themes/win95/theme.yml deleted file mode 100644 index c4ac8aa55..000000000 --- a/app/javascript/themes/win95/theme.yml +++ /dev/null @@ -1,18 +0,0 @@ -# win95 theme. - -# Ported over from `cybrespace:mastodon/theme_win95`. -# - -# You can use this theme file as inspiration for porting over -# a preëxisting Mastodon theme. - -# We only modify the `common` pack, which contains our styling. -pack: - common: - filename: index.js - stylesheet: true - # All unspecified packs will inherit from glitch. - -# The `fallback` parameter tells us to use glitch files for everything -# we haven't specified. -fallback: glitch diff --git a/app/lib/themes.rb b/app/lib/themes.rb index 7ced9f945..0819e2c90 100644 --- a/app/lib/themes.rb +++ b/app/lib/themes.rb @@ -17,10 +17,31 @@ class Themes name = File.basename(File.dirname(path)) if data['pack'] data['name'] = name + data['skin'] = { 'default' => [] } result[name] = data end end + Dir.glob(Rails.root.join('app', 'javascript', 'skins', '*', '*')) do |path| + ext = File.extname(path) + skin = File.basename(path) + name = File.basename(File.dirname(path)) + if result[name] + if File.directory?(path) + pack = [] + Dir.glob(File.join(path, '*.{css,scss}')) do |sheet| + pack.push(File.basename(sheet, File.extname(sheet))) + end + elsif ext.match?(/^\.s?css$/i) + skin = File.basename(path, ext) + pack = ['common'] + end + if skin != 'default' + result[name]['skin'][skin] = pack + end + end + end + @core = core @conf = result @@ -37,4 +58,8 @@ class Themes def names @conf.keys end + + def skins_for(name) + @conf[name]['skin'].keys + end end diff --git a/app/lib/user_settings_decorator.rb b/app/lib/user_settings_decorator.rb index d86959c0b..730c70177 100644 --- a/app/lib/user_settings_decorator.rb +++ b/app/lib/user_settings_decorator.rb @@ -27,6 +27,7 @@ class UserSettingsDecorator user.settings['system_font_ui'] = system_font_ui_preference if change?('setting_system_font_ui') user.settings['noindex'] = noindex_preference if change?('setting_noindex') user.settings['theme'] = theme_preference if change?('setting_theme') + user.settings['skin'] = skin_preference if change?('setting_skin') end def merged_notification_emails @@ -76,7 +77,11 @@ class UserSettingsDecorator def theme_preference settings['setting_theme'] end - + + def skin_preference + settings['setting_skin'] + end + def boolean_cast_setting(key) settings[key] == '1' end diff --git a/app/models/user.rb b/app/models/user.rb index b9b228c00..1d42d4f70 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -74,7 +74,7 @@ class User < ApplicationRecord has_many :session_activations, dependent: :destroy delegate :auto_play_gif, :default_sensitive, :unfollow_modal, :boost_modal, :delete_modal, - :reduce_motion, :system_font_ui, :noindex, :theme, + :reduce_motion, :system_font_ui, :noindex, :theme, :skin, to: :settings, prefix: :setting, allow_nil: false def confirmed? diff --git a/app/views/layouts/_theme.html.haml b/app/views/layouts/_theme.html.haml index cdec4b370..941ccc914 100644 --- a/app/views/layouts/_theme.html.haml +++ b/app/views/layouts/_theme.html.haml @@ -3,8 +3,11 @@ = render partial: 'layouts/theme', object: theme[:common] - if theme[:pack] = javascript_pack_tag theme[:name] ? "themes/#{theme[:name]}/#{theme[:pack]}" : "core/#{theme[:pack]}", integrity: true, crossorigin: 'anonymous' - - if theme[:stylesheet] - = stylesheet_pack_tag theme[:name] ? "themes/#{theme[:name]}/#{theme[:pack]}" : "core/#{theme[:pack]}", integrity: true, media: 'all' + - if theme[:skin] + - if !theme[:name] || theme[:skin] == 'default' + = stylesheet_pack_tag theme[:name] ? "themes/#{theme[:name]}/#{theme[:pack]}" : "core/#{theme[:pack]}", integrity: true, media: 'all' + - else + = stylesheet_pack_tag "skins/#{theme[:name]}/#{theme[:skin]}/#{theme[:pack]}" - if theme[:preload] - theme[:preload].each do |link| %link{ href: asset_pack_path("#{link}.js"), crossorigin: 'anonymous', rel: 'preload', as: 'script' }/ diff --git a/app/views/settings/preferences/show.html.haml b/app/views/settings/preferences/show.html.haml index 69e26a7be..0d487d9f3 100644 --- a/app/views/settings/preferences/show.html.haml +++ b/app/views/settings/preferences/show.html.haml @@ -28,6 +28,7 @@ .fields-group - if Themes.instance.names.size > 1 = f.input :setting_theme, collection: Themes.instance.names, label_method: lambda { |theme| I18n.t("themes.#{theme}", default: theme) }, wrapper: :with_label, include_blank: false + = f.input :setting_skin, collection: Themes.instance.skins_for(current_theme), label_method: lambda { |skin| I18n.t("themes.#{current_theme}.skins.#{skin}", default: skin) }, wrapper: :with_label, include_blank: false = f.input :setting_unfollow_modal, as: :boolean, wrapper: :with_label = f.input :setting_boost_modal, as: :boolean, wrapper: :with_label diff --git a/config/locales/simple_form.en.yml b/config/locales/simple_form.en.yml index faf41f316..b9ef21fef 100644 --- a/config/locales/simple_form.en.yml +++ b/config/locales/simple_form.en.yml @@ -15,6 +15,7 @@ en: other: %{count} characters left setting_noindex: Affects your public profile and status pages setting_theme: Affects how Mastodon looks when you're logged in from any device. + setting_skin: Reskins the selected Mastodon theme imports: data: CSV file exported from another Mastodon instance sessions: @@ -47,6 +48,7 @@ en: setting_reduce_motion: Reduce motion in animations setting_system_font_ui: Use system's default font setting_theme: Site theme + setting_skin: Skin setting_unfollow_modal: Show confirmation dialog before unfollowing someone severity: Severity type: Import type diff --git a/config/settings.yml b/config/settings.yml index 6983484d0..5abf647e8 100644 --- a/config/settings.yml +++ b/config/settings.yml @@ -26,6 +26,7 @@ defaults: &defaults system_font_ui: false noindex: false theme: 'glitch' + skin: 'default' notification_emails: follow: false reblog: false diff --git a/config/webpack/configuration.js b/config/webpack/configuration.js index f8741c5d8..cb31c6ab8 100644 --- a/config/webpack/configuration.js +++ b/config/webpack/configuration.js @@ -1,15 +1,16 @@ // Common configuration for webpacker loaded from config/webpacker.yml -const { basename, dirname, join, resolve } = require('path'); +const { basename, dirname, extname, join, resolve } = require('path'); const { env } = require('process'); const { safeLoad } = require('js-yaml'); -const { readFileSync } = require('fs'); +const { lstatSync, readFileSync } = require('fs'); const glob = require('glob'); const configPath = resolve('config', 'webpacker.yml'); const loadersDir = join(__dirname, 'loaders'); const settings = safeLoad(readFileSync(configPath), 'utf8')[env.NODE_ENV]; const themeFiles = glob.sync('app/javascript/themes/*/theme.yml'); +const skinFiles = glob.sync('app/javascript/skins/*/*'); const themes = {}; const core = function () { @@ -25,14 +26,35 @@ for (let i = 0; i < themeFiles.length; i++) { const themeFile = themeFiles[i]; const data = safeLoad(readFileSync(themeFile), 'utf8'); data.name = basename(dirname(themeFile)); + data.skin = {}; if (!data.pack_directory) { data.pack_directory = dirname(themeFile); } - if (data.pack && typeof data.pack == 'object') { + if (data.pack && typeof data.pack === 'object') { themes[data.name] = data; } } +for (let i = 0; i < skinFiles.length; i++) { + const skinFile = skinFiles[i]; + let skin = basename(skinFile); + const name = basename(dirname(skinFile)); + if (!themes[name]) { + continue; + } + const data = themes[name].skin; + if (lstatSync(skinFile).isDirectory()) { + data[skin] = {}; + const skinPacks = glob.sync(skinFile, '*.{css,scss}'); + for (let j = 0; j < skinPacks.length; j++) { + const pack = skinPacks[i]; + data[skin][basename(pack, extname(pack))] = pack; + } + } else if ((skin = skin.match(/^(.*)\.s?css$/i))) { + data[skin[1]] = { common: skinFile }; + } +} + function removeOuterSlashes(string) { return string.replace(/^\/*/, '').replace(/\/*$/, ''); } diff --git a/config/webpack/shared.js b/config/webpack/shared.js index 5b90f27fb..a2550bc81 100644 --- a/config/webpack/shared.js +++ b/config/webpack/shared.js @@ -1,7 +1,7 @@ // Note: You must restart bin/webpack-dev-server for changes to take effect const webpack = require('webpack'); -const { basename, dirname, join, relative, resolve } = require('path'); +const { basename, join, resolve } = require('path'); const { sync } = require('glob'); const ExtractTextPlugin = require('extract-text-webpack-plugin'); const ManifestPlugin = require('webpack-manifest-plugin'); @@ -24,6 +24,24 @@ function reducePacks (data, into = {}) { } return map; }, into); + if (data.name) { + Object.keys(data.skin).reduce((map, entry) => { + const skin = data.skin[entry]; + const skinName = entry; + if (!skin) { + return map; + } + Object.keys(skin).reduce((map, entry) => { + const packFile = skin[entry]; + if (!packFile) { + return map; + } + map[`skins/${data.name}/${skinName}/${entry}`] = resolve(packFile); + return map; + }, into); + return map; + }, into); + } return into; } -- cgit