From 5ed9556b8770719c96a96f1f7e769a5e75c0be41 Mon Sep 17 00:00:00 2001 From: Allan Wang Date: Tue, 20 Jun 2023 17:56:44 -0700 Subject: [PATCH 1/6] Copy themes --- app-compose/src/web/scss/core/_base.scss | 129 ++++++++++++++++++ app-compose/src/web/scss/core/_colors.scss | 19 +++ app-compose/src/web/scss/core/_core_vars.scss | 49 +++++++ .../src/web/scss/facebook/core/_core_bg.scss | 91 ++++++++++++ .../web/scss/facebook/core/_core_border.scss | 106 ++++++++++++++ .../web/scss/facebook/core/_core_hider.scss | 4 + .../scss/facebook/core/_core_messages.scss | 20 +++ .../web/scss/facebook/core/_core_text.scss | 44 ++++++ .../src/web/scss/facebook/core/_main.scss | 6 + .../src/web/scss/facebook/core/_svg.scss | 74 ++++++++++ .../src/web/scss/facebook/core/core.scss | 51 +++++++ .../src/web/scss/facebook/themes/.gitignore | 1 + .../src/web/scss/facebook/themes/custom.scss | 2 + .../src/web/scss/facebook/themes/default.scss | 1 + .../scss/facebook/themes/material_amoled.scss | 2 + .../scss/facebook/themes/material_dark.scss | 2 + .../scss/facebook/themes/material_glass.scss | 2 + .../scss/facebook/themes/material_light.scss | 2 + .../src/web/scss/messenger/core/_core_bg.scss | 4 + .../web/scss/messenger/core/_core_border.scss | 3 + .../web/scss/messenger/core/_core_hider.scss | 15 ++ .../web/scss/messenger/core/_core_text.scss | 3 + .../src/web/scss/messenger/core/_main.scss | 3 + .../src/web/scss/messenger/core/core.scss | 11 ++ .../src/web/scss/messenger/themes/.gitignore | 1 + .../src/web/scss/messenger/themes/custom.scss | 2 + .../web/scss/messenger/themes/default.scss | 1 + .../messenger/themes/material_amoled.scss | 2 + .../scss/messenger/themes/material_dark.scss | 2 + .../scss/messenger/themes/material_glass.scss | 2 + .../scss/messenger/themes/material_light.scss | 2 + app-compose/src/web/scss/palette/_custom.scss | 13 ++ .../web/scss/palette/_material_amoled.scss | 9 ++ .../src/web/scss/palette/_material_dark.scss | 8 ++ .../src/web/scss/palette/_material_glass.scss | 8 ++ .../src/web/scss/palette/_material_light.scss | 13 ++ 36 files changed, 707 insertions(+) create mode 100644 app-compose/src/web/scss/core/_base.scss create mode 100644 app-compose/src/web/scss/core/_colors.scss create mode 100644 app-compose/src/web/scss/core/_core_vars.scss create mode 100644 app-compose/src/web/scss/facebook/core/_core_bg.scss create mode 100644 app-compose/src/web/scss/facebook/core/_core_border.scss create mode 100644 app-compose/src/web/scss/facebook/core/_core_hider.scss create mode 100644 app-compose/src/web/scss/facebook/core/_core_messages.scss create mode 100644 app-compose/src/web/scss/facebook/core/_core_text.scss create mode 100644 app-compose/src/web/scss/facebook/core/_main.scss create mode 100644 app-compose/src/web/scss/facebook/core/_svg.scss create mode 100644 app-compose/src/web/scss/facebook/core/core.scss create mode 100644 app-compose/src/web/scss/facebook/themes/.gitignore create mode 100644 app-compose/src/web/scss/facebook/themes/custom.scss create mode 100644 app-compose/src/web/scss/facebook/themes/default.scss create mode 100644 app-compose/src/web/scss/facebook/themes/material_amoled.scss create mode 100644 app-compose/src/web/scss/facebook/themes/material_dark.scss create mode 100644 app-compose/src/web/scss/facebook/themes/material_glass.scss create mode 100644 app-compose/src/web/scss/facebook/themes/material_light.scss create mode 100644 app-compose/src/web/scss/messenger/core/_core_bg.scss create mode 100644 app-compose/src/web/scss/messenger/core/_core_border.scss create mode 100644 app-compose/src/web/scss/messenger/core/_core_hider.scss create mode 100644 app-compose/src/web/scss/messenger/core/_core_text.scss create mode 100644 app-compose/src/web/scss/messenger/core/_main.scss create mode 100644 app-compose/src/web/scss/messenger/core/core.scss create mode 100644 app-compose/src/web/scss/messenger/themes/.gitignore create mode 100644 app-compose/src/web/scss/messenger/themes/custom.scss create mode 100644 app-compose/src/web/scss/messenger/themes/default.scss create mode 100644 app-compose/src/web/scss/messenger/themes/material_amoled.scss create mode 100644 app-compose/src/web/scss/messenger/themes/material_dark.scss create mode 100644 app-compose/src/web/scss/messenger/themes/material_glass.scss create mode 100644 app-compose/src/web/scss/messenger/themes/material_light.scss create mode 100644 app-compose/src/web/scss/palette/_custom.scss create mode 100644 app-compose/src/web/scss/palette/_material_amoled.scss create mode 100644 app-compose/src/web/scss/palette/_material_dark.scss create mode 100644 app-compose/src/web/scss/palette/_material_glass.scss create mode 100644 app-compose/src/web/scss/palette/_material_light.scss diff --git a/app-compose/src/web/scss/core/_base.scss b/app-compose/src/web/scss/core/_base.scss new file mode 100644 index 000000000..8f233119d --- /dev/null +++ b/app-compose/src/web/scss/core/_base.scss @@ -0,0 +1,129 @@ +@use 'sass:math'; + +@mixin placeholder { + ::placeholder { + @content; + } + + ::-webkit-input-placeholder { + @content; + } + + :-moz-placeholder { + @content; + } + + ::-moz-placeholder { + @content; + } + + :-ms-input-placeholder { + @content; + } + + ::-ms-input-placeholder { + @content; + } +} + +@mixin fill-available { + width: 100%; + max-width: -webkit-fill-available; + max-width: -moz-available; + max-width: fill-available; +} + +@mixin keyframes($name) { + @-webkit-keyframes #{$name} { + @content; + } + + @-moz-keyframes #{$name} { + @content; + } + + //@-ms-keyframes #{$name} { + // @content; + //} + + @keyframes #{$name} { + @content; + } +} + +// Helper function to replace characters in a string +@function str-replace($string, $search, $replace: "") { + $index: str-index($string, $search); + + @return if( + $index, + str-slice($string, 1, $index - 1) + $replace + + str-replace(str-slice($string, $index + str-length($search)), $search, $replace), + $string + ); +} + +// https://css-tricks.com/probably-dont-base64-svg/ +// SVG optimization thanks to https://codepen.io/jakob-e/pen/doMoML +// Function to create an optimized svg url +// Version: 1.0.6 +@function svg-url($svg) { + // + // Add missing namespace + // + @if not str-index($svg, xmlns) { + $svg: str-replace($svg, "", "%3E"); + + // + // The maybe list + // + // Keep size and compile time down + // ... only add on documented fail + // + // $chunk: str-replace($chunk, '&', '%26'); + // $chunk: str-replace($chunk, '|', '%7C'); + // $chunk: str-replace($chunk, '[', '%5B'); + // $chunk: str-replace($chunk, ']', '%5D'); + // $chunk: str-replace($chunk, '^', '%5E'); + // $chunk: str-replace($chunk, '`', '%60'); + // $chunk: str-replace($chunk, ';', '%3B'); + // $chunk: str-replace($chunk, '?', '%3F'); + // $chunk: str-replace($chunk, ':', '%3A'); + // $chunk: str-replace($chunk, '@', '%40'); + // $chunk: str-replace($chunk, '=', '%3D'); + + $encoded: #{$encoded}#{$chunk}; + $index: $index + $slice; + } + + @return url("data:image/svg+xml,#{$encoded}"); +} + +// Background svg mixin +@mixin background-svg($svg, $extra: "no-repeat") { + background: svg-url($svg) unquote($extra) !important; +} diff --git a/app-compose/src/web/scss/core/_colors.scss b/app-compose/src/web/scss/core/_colors.scss new file mode 100644 index 000000000..ea75cc158 --- /dev/null +++ b/app-compose/src/web/scss/core/_colors.scss @@ -0,0 +1,19 @@ +$bg_transparent: rgba(#f0f, 0.02) !default; + +//Keep above as first line so partials aren't compiled +//Our default colors are test colors; production files should always import the actual colors + +$text: #d7b0d7 !default; +$text_disabled: rgba($text, 0.6) !default; +// must be visible with accent as the background +$accent_text: #76d7c2 !default; +$link: #9266d5 !default; +$accent: #980008 !default; +$background: #451515 !default; +// background2 must be transparent +$background2: rgba(lighten($background, 35%), 0.35) !default; //Also change ratio in material_light +$bg_opaque: rgba($background, 1.0) !default; +$bg_opaque2: rgba($background2, 1.0) !default; +$card: #239645 !default; +$tint: #ff4682 !default; // must be different from $background +$divider: rgba($text, 0.3) !default; diff --git a/app-compose/src/web/scss/core/_core_vars.scss b/app-compose/src/web/scss/core/_core_vars.scss new file mode 100644 index 000000000..9b2e43f54 --- /dev/null +++ b/app-compose/src/web/scss/core/_core_vars.scss @@ -0,0 +1,49 @@ +:root, .__fb-light-mode { + --accent: #{$accent} !important; + --attachment-footer-background: #{$bg_opaque} !important; + --background-deemphasized: #{$background} !important; + --card-background: #{$bg_opaque} !important; + --card-background-flat: #{$bg_opaque} !important; + --comment-background: #{$bg_opaque2} !important; + --comment-footer-background: #{$bg_opaque} !important; + --darkreader-bg--fds-white: #{$bg_opaque} !important; + --darkreader-text--primary-text: #{$text} !important; + --darkreader-text--secondary-text: #{$text} !important; + --disabled-button-background: #{$divider} !important; + --disabled-button-text: #{$text_disabled} !important; + --disabled-icon: #{$text_disabled} !important; + --disabled-text: #{$text_disabled} !important; + --divider: #{$divider} !important; + --event-date: #{$accent} !important; + --fds-white: #{$background} !important; + --glimmer-spinner-icon: #{$accent} !important; + --hero-banner-background: #{$bg_opaque2} !important; + --highlight-bg: #{$bg_opaque2} !important; + --media-outer-border: #{$divider} !important; + --messenger-card-background: #{$bg_opaque} !important; // Main background; needs to be opaque to hide gradient used for sender card + --messenger-reply-background: #{$bg_opaque2} !important; + --nav-bar-background: #{$bg_opaque} !important; + --new-notification-background: #{$background2} !important; + --placeholder-icon: #{$text} !important; + --popover-background: #{$bg_opaque2} !important; + --primary-icon: #{$text} !important; + --primary-text: #{$text} !important; + --primary-button-text: #{$text} !important; + --primary-deemphasized-button-background: #{$divider} !important; + --primary-text-on-media: #{$text} !important; + --scroll-thumb: #{$accent} !important; + --secondary-icon: #{$text} !important; + --secondary-text: #{$text} !important; + --section-header-text: #{$text} !important; + --nav-bar-background-gradient-wash: #{$bg_opaque} !important; + --nav-bar-background-gradient: #{$bg_opaque} !important; + --placeholder-text: #{$text} !important; // Date + --surface-background: #{$bg_opaque2} !important; // Emoji background + --toggle-active-background: #{$bg_opaque2} !important; + --wash: #{$bg_opaque2} !important; + --web-wash: #{$bg_opaque2} !important; + + [role="navigation"] { + --surface-background: #{$bg_opaque} !important; // Nav background + } +} diff --git a/app-compose/src/web/scss/facebook/core/_core_bg.scss b/app-compose/src/web/scss/facebook/core/_core_bg.scss new file mode 100644 index 000000000..5245a29ed --- /dev/null +++ b/app-compose/src/web/scss/facebook/core/_core_bg.scss @@ -0,0 +1,91 @@ +#viewport { + background: $background !important; +} + +html, body, :root, #root, #header, #MComposer, ._1upc, input, ._2f9r, ._59e9, ._5pz4, ._5lp4, +[style*="background-color: #FFFFFF"], [style*="background-color: #E4E6EB"], ._9drh, +._5lp5, .container, .subpage, ._5n_f, #static_templates, ._22_8, ._1t4h, ._uoq, ._3qdh, ._8ca, ._3h8i, +._6-l ._2us7, ._6-l ._6-p:not([style*="background-image:"]), ._333v, div.sharerSelector, ._529j, ._305j, ._1pph, ._3t_l, ._4pvz, +._1g05, .acy, ._51-g, ._533c, ._ib-, .sharerAttachmentEmpty, .sharerBottomWrapper, ._24e1, ._7g4m, ._bub, +._3bg5 ._56do, ._5hfh, ._52e-, .mQuestionsPollResultsBar, ._5hoc, ._5oxw, ._32_4, ._1hiz, ._53_-, ._4ut9, +._38do, .bo, .cq, ._234-, ._a-5, ._2zh4, ._15ks, ._3oyc, ._36dc, ._3iyw ._3iyx, ._6bes, ._55wo, ._4-dy, +.tlBody, #timelineBody, .timelineX, .timeline, .feed, .tlPrelude, .tlFeedPlaceholder, ._4_d0, ._2wn5, +.al, ._1gkq, ._5c5b, ._1qxg, ._5luf, ._2new, ._cld, ._3zvb, ._2nk0, .btnD, .btnI, ._2bdb, ._3ci9, ._2_gy, +._11ub, ._5p7j, ._55wm, ._5rgs, ._5xuj, ._1sv1, ._45fu, ._18qg, ._1_ac, ._5w3g, ._3e18, ._6be7, ._-kp, ._-kq, +._5q_r, ._5yt8, ._idb, ._2ip_, ._f6s, ._2l5v, ._8i2, ._kr5, ._2q7u, ._2q7v, ._5xp2, div.fullwidthMore, +._577z, ._2u4w, ._3u9p, ._3u9t, ._cw4, ._5_y-, ._5_y_, ._5_z3, ._cwy, ._5_z0, ._voz, ._vos, ._7i8m, +._5_z1, ._5_z2, ._2mtc, ._206a, ._1_-1, ._1ybg, .appCenterCategorySelectorButton, ._5_ee, ._3clk, +._5c9u, div._5y57::before, ._59f6._55so::before, .structuredPublisher, ._94v, ._vqv, ._8r-n, +._5lp5, ._1ho1, ._39y9._39ya, ._59_m, ._6rny, ._9sh-, ._1zep, ._5snt, ._5fn5, ._5rmd, ._7nya, +._55wm, ._2om3, ._2ol-, ._1f9d, ._vee, ._31a-, ._3r8b, ._3r9d, ._5vq5, ._3tl8, ._65wz, ._4edl, +.acw, ._4_xl, ._1p70, ._1p70, ._1ih_, ._51v6, ._u2c, ._484w, ._3ils, ._rm7, ._32qk, ._d01, ._1glm, +._ue6, ._hdn._hdn, ._6vzw, ._77xj, ._38nq, ._9_7, ._51li, ._7hkf, ._6vzz, ._3iyw ._37fb, ._5cqb, +._2y60, ._5fu3, ._2foa, ._2y5_, ._38o9, ._1kb, .mAppCenterFatLabel, ._3bmj, ._5zmb, ._2x2s, ._3kac, ._3kad, +._3f50, .mentions-placeholder, .mentions, .mentions-measurer, .acg, ._59tu, ._7lcm, ._7kxh, ._6rvp, ._6rvq, ._6rvk, +._4l9b, ._4gj3, .groupChromeView, ._i3g, ._3jcf, .error, ._1dbp, ._5zma, ._6beq, ._vi6, +._uww, textarea, ._15n_, ._skt, ._5f28, ._14_j, ._3bg5, ._53_-, ._52x1, ._35au, ._cwy, +._1rfn ._1rfk ._4vc-, ._1rfk, ._1rfk ._2v9s, ._301x { + background: $bg_transparent !important; +} + +// card related +._31nf, ._2v9s, ._d4i, article._55wo, ._10c_, ._2jl2, ._6150, ._50mi, ._4-dw, ._4_2z, ._5m_s, ._13fn, ._7kxe, [style*="background-color: #F5F8FF"], +._84lx, ._517h, ._59pe:focus, ._59pe:hover, ._m_1, ._3eqz, ._6m2, ._6q-c, ._61r- { + background: $card !important; +} + +// unread related + +.aclb { + background: $tint !important; +} + +// contains images so must have background-color +._cv_, ._2sq8 { + background-color: $bg_transparent !important; +} + +#page, ._8l7, ._-j8, ._-j9, ._6o5v, ._uwx, .touch ._uwx.mentions-input { + background: transparent !important; +} + +.jewel, .flyout, ._52z5, ._13e_, ._5-lw, ._5c0e, .jx-result, ._336p, .mentions-suggest-item, ._2suk, ._-j7, ._4d0v, ._4d0m, +.mentions-suggest, ._1xoz, ._1xow, ._14v5 ._14v8, ._8s4y, ._55ws, ._6j_d, +// desktop sharing page +.uiBoxLightblue, .uiBoxWhite, .uiBoxGray, .uiTokenizer, .uiTypeahead, ._558b ._54ng, ._2_bh, ._509o, ._509o:hover { + background: $bg_opaque !important; +} + +._403n, ._1-kc { + background: $bg_opaque2 !important; +} + +button:not([style*=image]):not(.privacyButtons), button::before, .touch ._56bt, ._56be::before, .btnS, .touch::before, +._590n, ._4g8h, ._2cpp, ._58a0.touched:after, ._7hfd, +.timeline .timelinePublisher, .touched, .sharerAttachment, +.item a.primary.touched .primarywrap, ._537a, ._7cui, ._785, +._5xo2, ._5u5a::before, ._4u3j, ._15ks, ._5hua, ._59tt, ._41ft, .jx-tokenizer, ._55fj, +.excessItem, .acr, ._5-lx, ._3g9-, ._6dsj ._3gin, ._69aj, +._4e8n, ._5pxa._3uj9, ._5n_5, ._u2d, ._56bu::before, ._5h8f, ._d00, ._2066, ._2k51, +._10sb li.selected, ._2z4j, ._ib-, ._1bhl, ._5a5j, ._6--d, ._77p7, +._2b06, ._2tsf, ._3gka, .mCount, ._27vc, ._4pv-, ._6pk5, ._86nt, +._4qax, ._4756, ._w34, ._56bv::before, ._5769, ._34iv, ._z-w, ._t21, .mToken, +#addMembersTypeahead .mToken.mTokenWeakReference, ._4_d0 ._8-89, +.acbk { + background: $background2 !important; +} + +.mQuestionsPollResultsBar .shaded, ._1r00 { + background: $accent !important; +} + +._220g, ._1_y8:after, ._6pk6, ._9rc8, +._2zh4::before, ._2ip_ ._2zh4::before, ._2ip_ ._15kk::before, ._2ip_ ._15kk + ._4u3j::before, +._58a0:before, ._43mh::before, ._43mh::after, ._1_-1::before, ._1kmv:after, ._1_ac:before { + background: $divider !important; +} + +//fab +button ._v89 ._54k8._1fl1, ._7nyk, ._7nym, ._7nyn { + background: $accent !important; +} diff --git a/app-compose/src/web/scss/facebook/core/_core_border.scss b/app-compose/src/web/scss/facebook/core/_core_border.scss new file mode 100644 index 000000000..6772adcbf --- /dev/null +++ b/app-compose/src/web/scss/facebook/core/_core_border.scss @@ -0,0 +1,106 @@ +//border between like and comment +._15kl::before, ._37fd .inlineComposerButton, ._1hb:before, +._pfn ._pfo::before, +._5j35::after, ._2k4b, ._3to7, ._4nw8 { + border-left: 1px solid $divider !important; +} + +._4_d1, ._5cni, ._3jcq, ._1ho1 { + border-right: 1px solid $divider !important; +} + +//above see more +._1mx0, ._1rbr, ._5yt8, ._idb, ._cld, ._1e8h, ._z-w, ._1ha, ._1n8h ._1oby, ._5f99, ._2t39, +._2pbp, ._5rou:first-child, ._egf:first-child, ._io2, ._3qdi ._48_m::after, ._46dd::before, +._15n_, ._3-2-, ._27ve, ._2s20, ._gui, ._2s21 > *::after, ._32qk, ._d00, ._d01, ._38o9, +._3u9t, ._55fj, .mEventProfileSection.useBorder td, ._3ils, ._5as0, ._5as2, ._5-lw, ._5rmd, +._2s1_:before, ._143z::before, ._143z::after, ._4d0x, ._5_gz, ._5_ev, ._63ur, ._6pi8, +._52x1, ._3wjp, ._usq, ._2cul:before, ._13e_, .jewel .flyout, ._3bg5 ._52x6, ._56d8, .al { + border-top: 1px solid $divider !important; +} + +._15ny::after, ._z-w, ._8i2, ._2nk0, ._22_8, ._1t4h, ._37fd, ._1ha, ._3bg5 ._56do, ._8he, +._400s, ._5hoc, ._1bhn, ._5ag6, ._4pvz, ._31y5, ._7gxb, ._-kp, ._6_q3::after, ._3al1, ._4d0w, ._4d0k, +._301x, ._x08 ._x0a:after, ._36dc, ._6-l ._57jn, ._527k, ._g_k, ._7i8v, ._7k1c, ._2_gy, +._577z:not(:last-child) ._ygd, ._3u9u, ._3mgz, ._52x6, ._2066, ._5luf, ._2bdc, ._3ci9, ._7i-0, +.mAppCenterFatLabel, .appCenterCategorySelectorButton, ._1q6v, ._5q_r, ._5yt8, ._38do, ._38dt, +._ap1, ._52x1, ._59tu, ._usq, ._13e_, ._59f6._55so::before, ._4gj3, .error, ._35--, ._1wev, +.jx-result, ._1f9d, ._vef, ._55x2 > *, .al, ._44qk, ._5rgs, ._5xuj, ._1sv1, ._idb, ._5_g-, +._5lp5, ._3-2-, ._3to6, ._ir5, ._4nw6, ._4nwh, ._27ve, div._51v6::before, ._5hu6, ._2wn5, ._1ho1, ._1xk6, +._3c9h::before, ._2s20, ._gui, ._5jku, ._2foa, ._2y60, ._5fu3, ._4en9, ._1kb:not(:last-child) ._1kc, +._5pz4, ._5lp4, ._5lp5, ._5h6z, ._5h6x, ._2om4, ._5fjw > div, ._5fjv > :first-child, +._5fjw > :first-child { + border-bottom: 1px solid $divider !important; +} + +.item a.primary.touched .primarywrap, ._4dwt ._5y33, ._1ih_, ._5_50, ._6beq, ._69aj, ._3iyw ._37fb, ._9drh, +._5fjv, ._3on6, ._2u4w, ._2om3, ._2ol-, ._5fjw, ._4z83, ._1gkq, ._4-dy, ._bub { + border-top: 1px solid $divider !important; + border-bottom: 1px solid $divider !important; +} + +//friend card border +._d4i, ._f6s, .mentions-suggest-item, .mentions-suggest, .sharerAttachment, +.mToken, #addMembersTypeahead .mToken.mTokenWeakReference, .mQuestionsPollResultsBar, +._15q7, ._2q7v, ._4dwt ._16ii, ._3qdi::after, ._6q-c, ._61r-, +._2q7w, .acy, ._58ak, ._3t_l, ._4msa, ._3h8i, ._3clk, ._1kt6, ._1ksq, ._9sh-, +._1_y5, ._lr0, ._5hgt, ._2cpp, ._50uu, ._50uw, ._31yd, ._1e3d, ._3xz7, ._1xoz, +._4kcb, ._2lut, .jewel .touchable-notification.touched, .touchable-notification .touchable.touched, +.home-notification .touchable.touched, ._6beo ._6ber, ._7kxg, +._73ku ._73jw, ._6--d, ._26vk._56bt, ._3iyw ._2whz ._13-g, ._-jx, +._4e8n, ._uww, .mentions-placeholder, .mentions-shadow, .mentions-measurer, ._517h, ._59pe:focus, ._59pe:hover, +.uiBoxLightblue, .uiBoxWhite, ._558b ._54nc, +._5whq, ._59tt, ._41ft::after, .jx-tokenizer, ._3uqf, ._4756, ._1rrd, ._5n_f { + border: 1px solid $divider !important; +} + +.mQuestionsPollResultsBar .shaded, ._1027._13sm { + border: 1px solid $text !important; +} + +._3gka { + border: 1px dashed $divider !important; +} + +//link card bottom border +._4o58::after, .acr, ._t21, ._2bdb, ._4ks>li, +.acw, .aclb, ._4qax, ._5h8f { + border-color: $divider !important; +} + +// like, comment, share divider +._15ks ._15kl::before { + border-left: 1px solid transparent !important; +} + +._56bf, .touch .btn { + border-radius: 0 !important; + border: 0 !important; +} + +//page side tab layout +._2cis { + border-left: 10px solid $bg_transparent !important; + border-right: 10px solid $bg_transparent !important; +} + +._2cir.selected, ._42rv, ._5zma, ._2x2s { + border-bottom: 3px solid $text !important; +} + +._1ss6 { + border-left: 2px solid $text !important; +} + +._484w.selected > ._6zf, ._5kqs::after, ._3lvo ._5xum._5xuk, ._x0b { + border-bottom: 1px solid $text !important; +} + +._484w.selected ._6zf, ._7gxa, ._2wn2 { + border-bottom: 2px solid $accent !important; +} + +// Small face previews +.facepile .facepileItem.facepileItemOverLapped .facepileItemRound, .facepile .facepileItem.facepileItemOverLapped.facepileItemRound, .facepile .facepileItem.facepileItemOverLapped .facepileMoreDotsRound { + border: 2px solid $bg_opaque2 !important; +} \ No newline at end of file diff --git a/app-compose/src/web/scss/facebook/core/_core_hider.scss b/app-compose/src/web/scss/facebook/core/_core_hider.scss new file mode 100644 index 000000000..a166db3bd --- /dev/null +++ b/app-compose/src/web/scss/facebook/core/_core_hider.scss @@ -0,0 +1,4 @@ +[data-sigil=m_login_upsell], +[data-sigil="m-loading-indicator-animate m-loading-indicator-root"] { + display: none !important; +} \ No newline at end of file diff --git a/app-compose/src/web/scss/facebook/core/_core_messages.scss b/app-compose/src/web/scss/facebook/core/_core_messages.scss new file mode 100644 index 000000000..aa3c04eb8 --- /dev/null +++ b/app-compose/src/web/scss/facebook/core/_core_messages.scss @@ -0,0 +1,20 @@ +// Not all message related components are here; only the main ones. +// Borders for instance are merged into core_border + +// Other person's message bubble +._34ee { + background: $background2 !important; + color: $text !important; + +} + +// Your message bubble; order matters +._34em ._34ee { + background: $accent !important; + color: $accent_text !important; +} + +// Sticker page +._5as0, ._5cni, ._5as2 { + background: $bg_opaque !important; +} \ No newline at end of file diff --git a/app-compose/src/web/scss/facebook/core/_core_text.scss b/app-compose/src/web/scss/facebook/core/_core_text.scss new file mode 100644 index 000000000..b67f9c201 --- /dev/null +++ b/app-compose/src/web/scss/facebook/core/_core_text.scss @@ -0,0 +1,44 @@ +html, body, input, ._42rv, ._4qau, ._dwm .descArea, ._eu5, ._wn-, +._1tcc, ._3g9-, ._29z_, ._3xz7, ._ib-, ._3bg5 ._56dq, ._477i, ._2vxk, ._29e6, ._8wr8, ._52lz, +.touched *, ._1_yj, ._1_yl, ._4pj9, ._2bdc, ._3qdh ._3qdn ._3qdk, ._3qdk ._48_q, ._7iah, ._61mn ._61mo, +._z-z, ._z-v, ._1e8d, ._36nl, ._36nm, ._2_11, ._2_rf, ._2ip_, ._403p, .cq, ._usr, #mErrorView .message, +._5xu2, ._3ml8, ._3mla, ._50vk, ._1m2u, ._31y7, ._4kcb, ._1lf6, ._1lf5, ._7-1j, ._4ajz, ._m_1 ._2aha, +._1lf4, ._1hiz, ._xod, ._5ag5, ._zmk, ._3t_h, ._5lm6, ._3clv, ._3zlc, ._36rd, ._6oby, ._6_qk, ._9dr8, +._31zk, ._31zl, ._3xsa, ._3xs9, ._2-4s, ._2fzz ul, ._3z10, ._4mo, ._2om6, ._33r5, ._82y3, ._82y1, ._5rmf, +._43mh, .touch .btn, .fcg, button, ._52j9, ._52jb, ._52ja, ._5j35, ._ctg, ._5300, ._5302, ._5_o0, +._rnk, ._24u0, ._1g06, ._14ye, .fcb, ._56cz._56c_, ._1gk_, ._55fj, ._45fu, ._7kx4, ._20zd, ._egh, ._egi, +._18qg, ._1_ac, ._529p, ._4dwt ._1vh3, ._4a5f, ._23_t, ._2rzc, ._23_s, ._2rzd, ._6obp, ._2iiu, ._1s06, +._5aga, ._5ag9, ._537a, .acy, ._5ro_, ._6-l ._2us7, ._4mp, ._2b08, ._36e0, ._4-dy, ._55i1, ._2wn6, ._1zep, +._14v5 ._14v8, ._1440, ._1442, ._1448, ._4ks_, .mCount, ._27vc, ._24e1, ._2rbw, ._3iyw ._3mzw, ._9si9, +textarea:not([style*="color: rgb"]), ._24pi, ._4en9, ._1kb, ._5p7j, ._2klz, ._5780, ._5781, ._5782, ._5fn5, +._3u9u, ._3u9_, ._3u9s, ._1hcx, ._2066, ._1_-1, ._cv_, ._1nbx, ._2cuh, ._6--d, ._77p7, ._7h_g, ._vbw, +._4ms9, ._4ms5, ._4ms6, ._31b4, ._31b5, ._5q_r, ._idb, ._38d-, ._3n8y, ._38dt, ._3oyg, ._21dc, ._6j_c, ._7iz_, +.uiStickyPlaceholderInput .placeholder, .mTypeahead span, ._4_d0 ._8-8a, ._6r12, ._5hoa, ._8r-l, ._7nyk, ._7nym, ._7nyn, +._27vp, ._4nwe, ._4nw9, ._27vi, .appCenterAppInfo, .appCenterPermissions, ._6xqt, ._7cui, ._84lx [style*="color: rgb"], +._3c9l, ._3c9m, ._4jn_, ._32qt, ._3mom, ._3moo, ._-7o, ._d00, ._d01, ._559g, ._7cdj, ._1_yd, ._1_yc, +._2new, .appCenterCategorySelectorButton, ._1ksq, ._1kt6, ._6ber, ._mxb, ._3oyd, ._3gir, ._3gis, +div.sharerSelector, .footer, ._4pv_, ._1dbp, ._3kad, ._20zc, ._2i5v, ._2i5w, ._6zf, ._mhg, ._6r9_, +a, ._5fpq, ._4gux, ._3bg5 ._52x1, ._3bg5 ._52x2, ._6dsj ._3gin, ._hdn._hdn, ._3iyw ._2whz ._13-g, ._6p6u, ._6p6v, +.mentions-input:not([style*="color: rgb"]), .mentions-placeholder:not([style*="color: rgb"]), +.largeStatusBox .placeHolder, .fcw, ._2rgt, ._67i4 ._5hu6 ._59tt, ._2bu3, ._2bu4, ._1ii2, ._1ii3, +._5-7t, .fcl, ._4qas, .thread-title, .title, ._46pa, ._336p, ._1rrd, ._2om4, ._4yxo, ._6m3, ._6m7, ._6m3 ._1t62, +._3m1m, ._2om2, ._5n_e, .appListExplanation, ._5yt8, ._8he, ._2luw, ._5rgs, ._t86 ._t87, ._t86 ._t88, +h1, h2, h3, h4, h5, h6 { + color: $text !important; +} + +// Related to like buttons +a[data-sigil~="unlike"], a[style*="color: rgb(32, 120, 244)"], a[style*="color:#2078f4"], +strong > a, a > ._2vyr, ._15ks ._2q8z._2q8z, ._1e3e, .blueName, ._5kqs ._55sr, ._484w.selected ._6zf, ._6_qj, ._2wn3, +._by_, ._1r05 { + color: $accent !important; +} + +._42nf ._42ng { + color: transparent !important; +} + +// most links do not have a special color. We will highlight those in posts and messages +p > a, .msg span > a { + color: $link !important; +} diff --git a/app-compose/src/web/scss/facebook/core/_main.scss b/app-compose/src/web/scss/facebook/core/_main.scss new file mode 100644 index 000000000..3e972f93d --- /dev/null +++ b/app-compose/src/web/scss/facebook/core/_main.scss @@ -0,0 +1,6 @@ +@import "core"; +@import "svg"; + +//this file is used as the base for all themes +//given that svgs take a lot of characters, we won't compile them when testing +//therefore we use the core scss diff --git a/app-compose/src/web/scss/facebook/core/_svg.scss b/app-compose/src/web/scss/facebook/core/_svg.scss new file mode 100644 index 000000000..9ed25433c --- /dev/null +++ b/app-compose/src/web/scss/facebook/core/_svg.scss @@ -0,0 +1,74 @@ +// icons courtesy of https://material.io/icons/ + +$camera: ''; + +// status upload image +._50uu { + @include background-svg($camera); +} + +$video: ''; + +// status upload video +._50uw { + @include background-svg($video); +} + +$like: ''; +$like_selected: ''; + +// 2018/12/29 +// Previously ._15km ._15ko::before and ._15km ._15ko._77la::before; however, reaction changes no longer affect this element +// The robust measure seems to be the parent of a[data-sigil~="like-reaction-flyout"] along with [data-sigil~="like"] for an unliked post +// and [data-sigil~="unlike"] for a liked post +a._15ko::before { + @include background-svg($like); + background-position: center !important; +} + +a._15ko._77la::before { + @include background-svg($like_selected); + background-position: center !important; +} + +$comment: ''; + +._15km ._15kq::before { + @include background-svg($comment); + background-position: center !important; +} + +$share: ''; + +._15km ._15kr::before { + @include background-svg($share); + background-position: center !important; +} + +$more_horiz: ''; + +//$menus: ".sp_89zNula0Qh5", +//".sp_MP2OtCXORz9", +//".sp_NIWBacTn8LF", +//// 2018/12/31 +//".sp_9ZFVhnFyWsw", +//// 2019/01/03 +//".sp_SJIJjSlGEIO"; +// +//$menu_collector: (); +// +//@each $menu in $menus { +// $menu_collector: append($menu_collector, unquote('#{$menu}'), 'comma'); +// $menu_collector: append($menu_collector, unquote('#{$menu}_2x'), 'comma'); +// $menu_collector: append($menu_collector, unquote('#{$menu}_3x'), 'comma'); +//} +// +//#{$menu_collector} { +// @include background-svg($more_horiz); +// background-position: center !important; +//} + +.story_body_container i.img[data-sigil*="story-popup-context"] { + @include background-svg($more_horiz); + background-position: center !important; +} \ No newline at end of file diff --git a/app-compose/src/web/scss/facebook/core/core.scss b/app-compose/src/web/scss/facebook/core/core.scss new file mode 100644 index 000000000..f03cdd68f --- /dev/null +++ b/app-compose/src/web/scss/facebook/core/core.scss @@ -0,0 +1,51 @@ +@import "../../core/colors"; +@import "../../core/base"; +@import "../../core/core_vars"; +@import "core_text"; +@import "core_bg"; +@import "core_border"; +@import "core_messages"; +@import "core_hider"; + +//GLOBAL overrides; use with caution +*, *::after, *::before { + text-shadow: none !important; + box-shadow: none !important; +} + +// .touch .btnS, button, ._94v, ._590n { +// box-shadow: none !important; +// } + +@include placeholder { + color: $text_disabled !important; +} + +.excessItem { + outline: $divider !important; +} + +._3m1m { + background: linear-gradient(transparent, $bg_opaque) !important; +} + +//new comment +@include keyframes(highlightFade) { + 0%, 50% { + background: $background2; + } + + 100% { + background: $bg_transparent; + } +} + +@include keyframes(chatHighlightAnimation) { + 0%, 100% { + background: $bg_transparent; + } + + 50% { + background: $background2; + } +} diff --git a/app-compose/src/web/scss/facebook/themes/.gitignore b/app-compose/src/web/scss/facebook/themes/.gitignore new file mode 100644 index 000000000..4c46adff9 --- /dev/null +++ b/app-compose/src/web/scss/facebook/themes/.gitignore @@ -0,0 +1 @@ +test.scss diff --git a/app-compose/src/web/scss/facebook/themes/custom.scss b/app-compose/src/web/scss/facebook/themes/custom.scss new file mode 100644 index 000000000..ab3d0f34e --- /dev/null +++ b/app-compose/src/web/scss/facebook/themes/custom.scss @@ -0,0 +1,2 @@ +@import "../../palette/custom"; +@import "../core/main"; diff --git a/app-compose/src/web/scss/facebook/themes/default.scss b/app-compose/src/web/scss/facebook/themes/default.scss new file mode 100644 index 000000000..92e352a7d --- /dev/null +++ b/app-compose/src/web/scss/facebook/themes/default.scss @@ -0,0 +1 @@ +@import "../core/core_hider"; diff --git a/app-compose/src/web/scss/facebook/themes/material_amoled.scss b/app-compose/src/web/scss/facebook/themes/material_amoled.scss new file mode 100644 index 000000000..4b1b5d173 --- /dev/null +++ b/app-compose/src/web/scss/facebook/themes/material_amoled.scss @@ -0,0 +1,2 @@ +@import "../../palette/material_amoled"; +@import "../core/main"; diff --git a/app-compose/src/web/scss/facebook/themes/material_dark.scss b/app-compose/src/web/scss/facebook/themes/material_dark.scss new file mode 100644 index 000000000..badec66dc --- /dev/null +++ b/app-compose/src/web/scss/facebook/themes/material_dark.scss @@ -0,0 +1,2 @@ +@import "../../palette/material_dark"; +@import "../core/main"; diff --git a/app-compose/src/web/scss/facebook/themes/material_glass.scss b/app-compose/src/web/scss/facebook/themes/material_glass.scss new file mode 100644 index 000000000..480def7fc --- /dev/null +++ b/app-compose/src/web/scss/facebook/themes/material_glass.scss @@ -0,0 +1,2 @@ +@import "../../palette/material_glass"; +@import "../core/main"; diff --git a/app-compose/src/web/scss/facebook/themes/material_light.scss b/app-compose/src/web/scss/facebook/themes/material_light.scss new file mode 100644 index 000000000..2a84f4495 --- /dev/null +++ b/app-compose/src/web/scss/facebook/themes/material_light.scss @@ -0,0 +1,2 @@ +@import "../../palette/material_light"; +@import "../core/main"; \ No newline at end of file diff --git a/app-compose/src/web/scss/messenger/core/_core_bg.scss b/app-compose/src/web/scss/messenger/core/_core_bg.scss new file mode 100644 index 000000000..92d3dd858 --- /dev/null +++ b/app-compose/src/web/scss/messenger/core/_core_bg.scss @@ -0,0 +1,4 @@ +html, body, :root, #root, +[style*="background-color: #FFFFFF"], [style*="background-color: #E4E6EB"] { + background: $bg_transparent !important; +} diff --git a/app-compose/src/web/scss/messenger/core/_core_border.scss b/app-compose/src/web/scss/messenger/core/_core_border.scss new file mode 100644 index 000000000..10495a025 --- /dev/null +++ b/app-compose/src/web/scss/messenger/core/_core_border.scss @@ -0,0 +1,3 @@ +[role="navigation"] { + border-right: 2px solid $bg_opaque2 !important; +} \ No newline at end of file diff --git a/app-compose/src/web/scss/messenger/core/_core_hider.scss b/app-compose/src/web/scss/messenger/core/_core_hider.scss new file mode 100644 index 000000000..a2dbeb483 --- /dev/null +++ b/app-compose/src/web/scss/messenger/core/_core_hider.scss @@ -0,0 +1,15 @@ +// Sizing adjustments +[role="navigation"] { + .rq0escxv.l9j0dhe7.du4w35lb.j83agx80.g5gj957u.rj1gh0hx.buofh1pr.hpfvmrgz.i1fnvgqd.bp9cbjyn.owycx6da.btwxx1t3.dflh9lhu.scb9dxdr.sj5x9vvc.cxgpxx05.sn0e7ne5.f6rbj1fe.l3ldwz01 /* New! Messenger app for windows */, + .rq0escxv.l9j0dhe7.du4w35lb.n851cfcs.aahdfvyu /* Search messenger */, + .wkznzc2l /* Top left chat + menu entry */ { + display: none !important; + } +} + +header[role="banner"] /* login banner */, +._90px._9gb7 /* login bottom banner */, +.rq0escxv.l9j0dhe7.du4w35lb.j83agx80.cbu4d94t.pfnyh3mw.d2edcug0.hpfvmrgz.p8fzw8mz.pcp91wgn.iuny7tx3.ipjc6fyt /* Top bar call video info icons */, +.kuivcneq /* Right sidebar */ { + display: none !important; +} diff --git a/app-compose/src/web/scss/messenger/core/_core_text.scss b/app-compose/src/web/scss/messenger/core/_core_text.scss new file mode 100644 index 000000000..7409cc4e5 --- /dev/null +++ b/app-compose/src/web/scss/messenger/core/_core_text.scss @@ -0,0 +1,3 @@ +html, body, input { + color: $text !important; +} \ No newline at end of file diff --git a/app-compose/src/web/scss/messenger/core/_main.scss b/app-compose/src/web/scss/messenger/core/_main.scss new file mode 100644 index 000000000..e5da53773 --- /dev/null +++ b/app-compose/src/web/scss/messenger/core/_main.scss @@ -0,0 +1,3 @@ +@import "core"; + +//this file is used as the base for all messenger themes diff --git a/app-compose/src/web/scss/messenger/core/core.scss b/app-compose/src/web/scss/messenger/core/core.scss new file mode 100644 index 000000000..7cebadb57 --- /dev/null +++ b/app-compose/src/web/scss/messenger/core/core.scss @@ -0,0 +1,11 @@ +@import "../../core/colors"; +@import "../../core/base"; +@import "../../core/core_vars"; +@import "core_text"; +@import "core_bg"; +@import "core_border"; +@import "core_hider"; + +@include placeholder { + color: $text_disabled !important; +} \ No newline at end of file diff --git a/app-compose/src/web/scss/messenger/themes/.gitignore b/app-compose/src/web/scss/messenger/themes/.gitignore new file mode 100644 index 000000000..4c46adff9 --- /dev/null +++ b/app-compose/src/web/scss/messenger/themes/.gitignore @@ -0,0 +1 @@ +test.scss diff --git a/app-compose/src/web/scss/messenger/themes/custom.scss b/app-compose/src/web/scss/messenger/themes/custom.scss new file mode 100644 index 000000000..ab3d0f34e --- /dev/null +++ b/app-compose/src/web/scss/messenger/themes/custom.scss @@ -0,0 +1,2 @@ +@import "../../palette/custom"; +@import "../core/main"; diff --git a/app-compose/src/web/scss/messenger/themes/default.scss b/app-compose/src/web/scss/messenger/themes/default.scss new file mode 100644 index 000000000..92e352a7d --- /dev/null +++ b/app-compose/src/web/scss/messenger/themes/default.scss @@ -0,0 +1 @@ +@import "../core/core_hider"; diff --git a/app-compose/src/web/scss/messenger/themes/material_amoled.scss b/app-compose/src/web/scss/messenger/themes/material_amoled.scss new file mode 100644 index 000000000..4b1b5d173 --- /dev/null +++ b/app-compose/src/web/scss/messenger/themes/material_amoled.scss @@ -0,0 +1,2 @@ +@import "../../palette/material_amoled"; +@import "../core/main"; diff --git a/app-compose/src/web/scss/messenger/themes/material_dark.scss b/app-compose/src/web/scss/messenger/themes/material_dark.scss new file mode 100644 index 000000000..badec66dc --- /dev/null +++ b/app-compose/src/web/scss/messenger/themes/material_dark.scss @@ -0,0 +1,2 @@ +@import "../../palette/material_dark"; +@import "../core/main"; diff --git a/app-compose/src/web/scss/messenger/themes/material_glass.scss b/app-compose/src/web/scss/messenger/themes/material_glass.scss new file mode 100644 index 000000000..480def7fc --- /dev/null +++ b/app-compose/src/web/scss/messenger/themes/material_glass.scss @@ -0,0 +1,2 @@ +@import "../../palette/material_glass"; +@import "../core/main"; diff --git a/app-compose/src/web/scss/messenger/themes/material_light.scss b/app-compose/src/web/scss/messenger/themes/material_light.scss new file mode 100644 index 000000000..2a84f4495 --- /dev/null +++ b/app-compose/src/web/scss/messenger/themes/material_light.scss @@ -0,0 +1,2 @@ +@import "../../palette/material_light"; +@import "../core/main"; \ No newline at end of file diff --git a/app-compose/src/web/scss/palette/_custom.scss b/app-compose/src/web/scss/palette/_custom.scss new file mode 100644 index 000000000..0adb7768c --- /dev/null +++ b/app-compose/src/web/scss/palette/_custom.scss @@ -0,0 +1,13 @@ +$bg_transparent: unquote('$BT$'); +$text: unquote('$T$'); +$text_disabled: unquote('$TD$'); +$link: unquote('$TT$'); +$accent: unquote('$A$'); +$accent_text: unquote('$AT$'); +$background: unquote('$B$'); +$background2: unquote('$BBT$'); +$bg_opaque: unquote('$O$'); +$bg_opaque2: unquote('$OO$'); +$divider: unquote('$D$'); +$card: unquote('$C$'); +$tint: unquote('$TI$'); diff --git a/app-compose/src/web/scss/palette/_material_amoled.scss b/app-compose/src/web/scss/palette/_material_amoled.scss new file mode 100644 index 000000000..3eca36bc5 --- /dev/null +++ b/app-compose/src/web/scss/palette/_material_amoled.scss @@ -0,0 +1,9 @@ +$text: #fff; +$accent_text: #fff; +$link: #5d86dd; +$accent: #5d86dd; +$background: #000; +$background2: rgba($background, 0.35); +$bg_transparent: $background; +$card: $background2; +$tint: rgba(#fff, 0.2); diff --git a/app-compose/src/web/scss/palette/_material_dark.scss b/app-compose/src/web/scss/palette/_material_dark.scss new file mode 100644 index 000000000..a27627c07 --- /dev/null +++ b/app-compose/src/web/scss/palette/_material_dark.scss @@ -0,0 +1,8 @@ +$text: #fff; +$accent_text: #fff; +$link: #5d86dd; +$accent: #5d86dd; +$background: #303030; +$bg_transparent: $background; +$card: #353535; +$tint: rgba(#fff, 0.2); diff --git a/app-compose/src/web/scss/palette/_material_glass.scss b/app-compose/src/web/scss/palette/_material_glass.scss new file mode 100644 index 000000000..c9e399e7a --- /dev/null +++ b/app-compose/src/web/scss/palette/_material_glass.scss @@ -0,0 +1,8 @@ +$text: #fff; +$accent_text: #fff; +$link: #5d86dd; +$accent: #5d86dd; +$background: rgba(#000, 0.1); +$bg_transparent: transparent; +$card: rgba(#000, 0.25); +$tint: rgba(#fff, 0.15); \ No newline at end of file diff --git a/app-compose/src/web/scss/palette/_material_light.scss b/app-compose/src/web/scss/palette/_material_light.scss new file mode 100644 index 000000000..2a7991801 --- /dev/null +++ b/app-compose/src/web/scss/palette/_material_light.scss @@ -0,0 +1,13 @@ +$text: #000; +$accent_text: #fff; +$link: #3b5998; +$accent: #3b5998; +$background: #fafafa; +// this is actually the inverse of material light (bg should be gray, cards should be white), +// but it looks better than the alternative +$background2: rgba(darken($background, 8%), 0.35); + +$bg_transparent: $background; + +$card: #fff; +$tint: #ddd; \ No newline at end of file From c3023b0da95059636870b4ed8539dd8734566ba7 Mon Sep 17 00:00:00 2001 From: Allan Wang Date: Tue, 20 Jun 2023 20:28:29 -0700 Subject: [PATCH 2/6] Add most injectors --- app-compose/build.gradle | 4 + .../kotlin/com/pitchedapps/frost/FrostApp.kt | 12 ++ .../com/pitchedapps/frost/StartActivity.kt | 20 ++-- .../frost/web/FrostWebUiOptions.kt | 66 +++++++++++ .../frost/web/state/FrostMiddleware.kt | 18 ++- .../com/pitchedapps/frost/webview/FrostWeb.kt | 6 +- .../frost/webview/FrostWebComposer.kt | 4 +- .../frost/webview/FrostWebViewClients.kt | 10 +- .../webview/injection/FrostJsInjectors.kt | 66 +++++++++++ .../frost/webview/injection/JsInjector.kt | 108 ++++++++++++++++++ .../webview/injection/assets/CssActions.kt | 35 ++++++ .../webview/injection/assets/CssHider.kt | 63 ++++++++++ .../webview/injection/assets/JsActions.kt | 55 +++++++++ .../webview/injection/assets/JsAssets.kt | 77 +++++++++++++ 14 files changed, 527 insertions(+), 17 deletions(-) create mode 100644 app-compose/src/main/kotlin/com/pitchedapps/frost/web/FrostWebUiOptions.kt create mode 100644 app-compose/src/main/kotlin/com/pitchedapps/frost/webview/injection/FrostJsInjectors.kt create mode 100644 app-compose/src/main/kotlin/com/pitchedapps/frost/webview/injection/JsInjector.kt create mode 100644 app-compose/src/main/kotlin/com/pitchedapps/frost/webview/injection/assets/CssActions.kt create mode 100644 app-compose/src/main/kotlin/com/pitchedapps/frost/webview/injection/assets/CssHider.kt create mode 100644 app-compose/src/main/kotlin/com/pitchedapps/frost/webview/injection/assets/JsActions.kt create mode 100644 app-compose/src/main/kotlin/com/pitchedapps/frost/webview/injection/assets/JsAssets.kt diff --git a/app-compose/build.gradle b/app-compose/build.gradle index 3011b341b..28129fe3b 100644 --- a/app-compose/build.gradle +++ b/app-compose/build.gradle @@ -206,6 +206,10 @@ dependencies { implementation("com.google.protobuf:protobuf-kotlin-lite:3.23.3") implementation("app.cash.sqldelight:android-driver:2.0.0-rc01") + + // https://mvnrepository.com/artifact/org.apache.commons/commons-text + implementation("org.apache.commons:commons-text:1.10.0") + } protobuf { diff --git a/app-compose/src/main/kotlin/com/pitchedapps/frost/FrostApp.kt b/app-compose/src/main/kotlin/com/pitchedapps/frost/FrostApp.kt index f1dde26f6..9a1c4ebcd 100644 --- a/app-compose/src/main/kotlin/com/pitchedapps/frost/FrostApp.kt +++ b/app-compose/src/main/kotlin/com/pitchedapps/frost/FrostApp.kt @@ -21,15 +21,20 @@ import android.app.Application import android.os.Bundle import com.google.common.flogger.FluentLogger import com.pitchedapps.frost.hilt.FrostComponents +import com.pitchedapps.frost.webview.injection.FrostJsInjectors +import com.pitchedapps.frost.webview.injection.assets.JsAssets import dagger.hilt.android.HiltAndroidApp import javax.inject.Inject import javax.inject.Provider +import kotlinx.coroutines.MainScope +import kotlinx.coroutines.launch /** Frost Application. */ @HiltAndroidApp class FrostApp : Application() { @Inject lateinit var componentsProvider: Provider + @Inject lateinit var frostJsInjectors: Provider override fun onCreate() { super.onCreate() @@ -57,6 +62,13 @@ class FrostApp : Application() { }, ) } + + MainScope().launch { setup() } + } + + private suspend fun setup() { + JsAssets.load(this) + frostJsInjectors.get().load() } companion object { diff --git a/app-compose/src/main/kotlin/com/pitchedapps/frost/StartActivity.kt b/app-compose/src/main/kotlin/com/pitchedapps/frost/StartActivity.kt index 0e2098b1b..eb46ba46c 100644 --- a/app-compose/src/main/kotlin/com/pitchedapps/frost/StartActivity.kt +++ b/app-compose/src/main/kotlin/com/pitchedapps/frost/StartActivity.kt @@ -18,7 +18,7 @@ package com.pitchedapps.frost import android.content.Intent import android.os.Bundle -import androidx.appcompat.app.AppCompatActivity +import androidx.activity.ComponentActivity import androidx.lifecycle.lifecycleScope import com.google.common.flogger.FluentLogger import com.pitchedapps.frost.components.FrostDataStore @@ -46,7 +46,7 @@ import kotlinx.coroutines.withContext * and will launch another activity without history after doing initialization work. */ @AndroidEntryPoint -class StartActivity : AppCompatActivity() { +class StartActivity : ComponentActivity() { @Inject lateinit var frostDb: FrostDb @@ -65,14 +65,14 @@ class StartActivity : AppCompatActivity() { // TODO load real tabs store.dispatch(TabListAction.SetHomeTabs(data = listOf(FbItem.Feed, FbItem.Menu))) // Test something scrollable - store.dispatch( - TabAction( - tabId = HomeTabSessionState.homeTabId(0), - TabAction.ContentAction.UpdateUrlAction( - "https://github.com/AllanWang/Frost-for-Facebook" - ), - ), - ) + // store.dispatch( + // TabAction( + // tabId = HomeTabSessionState.homeTabId(0), + // TabAction.ContentAction.UpdateUrlAction( + // "https://github.com/AllanWang/Frost-for-Facebook" + // ), + // ), + // ) store.dispatch( TabAction( tabId = HomeTabSessionState.homeTabId(1), diff --git a/app-compose/src/main/kotlin/com/pitchedapps/frost/web/FrostWebUiOptions.kt b/app-compose/src/main/kotlin/com/pitchedapps/frost/web/FrostWebUiOptions.kt new file mode 100644 index 000000000..2b71635fc --- /dev/null +++ b/app-compose/src/main/kotlin/com/pitchedapps/frost/web/FrostWebUiOptions.kt @@ -0,0 +1,66 @@ +/* + * Copyright 2023 Allan Wang + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.pitchedapps.frost.web + +import com.pitchedapps.frost.components.FrostDataStore +import java.util.concurrent.atomic.AtomicBoolean +import javax.inject.Singleton + +/** Snapshot of UI options based on user preferences */ +interface FrostWebUiOptions { + + val theme: Theme + + enum class Theme { + Original, + Light, + Dark, + Amoled, + Glass // Custom + } +} + +/** + * Singleton to provide snapshots of [FrostWebUiOptions]. + * + * This is a mutable class, and does not provide change listeners. We will update activities + * manually when needed. + */ +@Singleton +class FrostWebUiSnapshot(private val dataStore: FrostDataStore) { + + @Volatile + var options: FrostWebUiOptions = defaultOptions() + private set + + private val stale = AtomicBoolean(true) + + private fun defaultOptions(): FrostWebUiOptions = + object : FrostWebUiOptions { + override val theme: FrostWebUiOptions.Theme = FrostWebUiOptions.Theme.Original + } + + /** Fetch new snapshot and update other singletons */ + suspend fun reload() { + if (!stale.getAndSet(false)) return + // todo load + } + + fun markAsStale() { + stale.set(true) + } +} diff --git a/app-compose/src/main/kotlin/com/pitchedapps/frost/web/state/FrostMiddleware.kt b/app-compose/src/main/kotlin/com/pitchedapps/frost/web/state/FrostMiddleware.kt index 66840c7fb..73287f866 100644 --- a/app-compose/src/main/kotlin/com/pitchedapps/frost/web/state/FrostMiddleware.kt +++ b/app-compose/src/main/kotlin/com/pitchedapps/frost/web/state/FrostMiddleware.kt @@ -28,10 +28,26 @@ class FrostLoggerMiddleware : FrostWebMiddleware { next: (FrostWebAction) -> Unit, action: FrostWebAction ) { - logger.atInfo().log("FrostWebAction: %s - %s", action::class.simpleName, action) + if (logInfo(action)) { + logger.atInfo().log("FrostWebAction: %s", action) + } else { + logger.atFine().log("FrostWebAction: %s", action) + } next(action) } + private fun logInfo(action: FrostWebAction): Boolean { + when (action) { + is TabAction -> + when (action.action) { + is TabAction.ContentAction.UpdateProgressAction -> return false + else -> {} + } + else -> {} + } + return true + } + companion object { private val logger = FluentLogger.forEnclosingClass() } diff --git a/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/FrostWeb.kt b/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/FrostWeb.kt index 0d7d0a40f..dc1232108 100644 --- a/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/FrostWeb.kt +++ b/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/FrostWeb.kt @@ -20,6 +20,7 @@ import android.webkit.WebViewClient import com.pitchedapps.frost.ext.WebTargetId import com.pitchedapps.frost.web.FrostWebHelper import com.pitchedapps.frost.web.state.FrostWebStore +import com.pitchedapps.frost.webview.injection.FrostJsInjectors import dagger.BindsInstance import dagger.Module import dagger.Provides @@ -62,8 +63,9 @@ internal object FrostWebModule { fun client( @FrostWeb tabId: WebTargetId, store: FrostWebStore, - webHelper: FrostWebHelper - ): WebViewClient = FrostWebViewClient(tabId, store, webHelper) + webHelper: FrostWebHelper, + frostJsInjectors: FrostJsInjectors, + ): WebViewClient = FrostWebViewClient(tabId, store, webHelper, frostJsInjectors) } /** diff --git a/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/FrostWebComposer.kt b/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/FrostWebComposer.kt index a33375806..9ca3860df 100644 --- a/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/FrostWebComposer.kt +++ b/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/FrostWebComposer.kt @@ -20,6 +20,7 @@ import com.pitchedapps.frost.compose.webview.FrostWebCompose import com.pitchedapps.frost.ext.WebTargetId import com.pitchedapps.frost.web.FrostWebHelper import com.pitchedapps.frost.web.state.FrostWebStore +import com.pitchedapps.frost.webview.injection.FrostJsInjectors import javax.inject.Inject class FrostWebComposer @@ -27,10 +28,11 @@ class FrostWebComposer internal constructor( private val store: FrostWebStore, private val webHelper: FrostWebHelper, + private val frostJsInjectors: FrostJsInjectors, ) { fun create(tabId: WebTargetId): FrostWebCompose { - val client = FrostWebViewClient(tabId, store, webHelper) + val client = FrostWebViewClient(tabId, store, webHelper, frostJsInjectors) val chromeClient = FrostChromeClient(tabId, store) return FrostWebCompose(tabId, store, client, chromeClient) } diff --git a/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/FrostWebViewClients.kt b/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/FrostWebViewClients.kt index a100319fb..a9bb3bc88 100644 --- a/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/FrostWebViewClients.kt +++ b/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/FrostWebViewClients.kt @@ -32,6 +32,7 @@ import com.pitchedapps.frost.web.state.TabAction import com.pitchedapps.frost.web.state.TabAction.ContentAction.UpdateNavigationAction import com.pitchedapps.frost.web.state.TabAction.ContentAction.UpdateProgressAction import com.pitchedapps.frost.web.state.TabAction.ContentAction.UpdateTitleAction +import com.pitchedapps.frost.webview.injection.FrostJsInjectors import java.io.ByteArrayInputStream /** @@ -63,7 +64,8 @@ abstract class BaseWebViewClient : WebViewClient() { class FrostWebViewClient( private val tabId: WebTargetId, private val store: FrostWebStore, - override val webHelper: FrostWebHelper + override val webHelper: FrostWebHelper, + private val frostJsInjectors: FrostJsInjectors, ) : BaseWebViewClient() { private fun FrostWebStore.dispatch(action: TabAction.Action) { @@ -140,8 +142,10 @@ class FrostWebViewClient( // ) // } - // override fun onPageCommitVisible(view: WebView, url: String?) { - // super.onPageCommitVisible(view, url) + override fun onPageCommitVisible(view: WebView, url: String?) { + super.onPageCommitVisible(view, url) + frostJsInjectors.injectOnPageCommitVisible(view, url) + } // injectBackgroundColor() // when { // url.isFacebookUrl -> { diff --git a/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/injection/FrostJsInjectors.kt b/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/injection/FrostJsInjectors.kt new file mode 100644 index 000000000..77cb6c59e --- /dev/null +++ b/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/injection/FrostJsInjectors.kt @@ -0,0 +1,66 @@ +/* + * Copyright 2023 Allan Wang + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.pitchedapps.frost.webview.injection + +import android.content.Context +import android.webkit.WebView +import com.google.common.flogger.FluentLogger +import com.pitchedapps.frost.webview.injection.assets.JsActions +import dagger.hilt.android.qualifiers.ApplicationContext +import java.io.BufferedReader +import java.io.FileNotFoundException +import javax.inject.Inject +import javax.inject.Singleton +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext + +@Singleton +class FrostJsInjectors +@Inject +internal constructor( + @ApplicationContext private val context: Context, +) { + + @Volatile private var theme: JsInjector = JsInjector.EMPTY + + fun injectOnPageCommitVisible(view: WebView, url: String?) { + logger.atInfo().log("inject page commit visible %b", theme != JsInjector.EMPTY) + theme.inject(view) + } + + fun getTheme(): JsInjector { + return try { + val content = + context.assets + .open("frostcore/css/facebook/themes/material_glass.css") + .bufferedReader() + .use(BufferedReader::readText) + JsBuilder().css(content).build() + } catch (e: FileNotFoundException) { + logger.atSevere().withCause(e).log("CssAssets file not found") + JsActions.EMPTY + } + } + + suspend fun load() { + withContext(Dispatchers.IO) { theme = getTheme() } + } + + companion object { + private val logger = FluentLogger.forEnclosingClass() + } +} diff --git a/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/injection/JsInjector.kt b/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/injection/JsInjector.kt new file mode 100644 index 000000000..28211e26d --- /dev/null +++ b/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/injection/JsInjector.kt @@ -0,0 +1,108 @@ +/* + * Copyright 2023 Allan Wang + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.pitchedapps.frost.webview.injection + +import android.webkit.WebView +import org.apache.commons.text.StringEscapeUtils + +interface JsInjector { + + fun inject(webView: WebView) + + companion object { + + val EMPTY: JsInjector = EmptyJsInjector + + operator fun invoke(content: String): JsInjector = + object : JsInjector { + override fun inject(webView: WebView) { + webView.evaluateJavascript(content, null) + } + } + } +} + +private object EmptyJsInjector : JsInjector { + override fun inject(webView: WebView) { + // Noop + } +} + +data class OneShotJsInjector(val tag: String, val injector: JsInjector) : JsInjector { + override fun inject(webView: WebView) { + // TODO + } +} + +class JsBuilder { + private val css = StringBuilder() + private val js = StringBuilder() + + private var tag: String? = null + + fun css(css: String): JsBuilder { + this.css.append(StringEscapeUtils.escapeEcmaScript(css)) + return this + } + + fun js(content: String): JsBuilder { + this.js.append(content) + return this + } + + fun single(tag: String): JsBuilder { + this.tag = tag // TODO TagObfuscator.obfuscateTag(tag) + return this + } + + fun build() = JsInjector(toString()) + + override fun toString(): String { + val tag = this.tag + val builder = + StringBuilder().apply { + append("!function(){") + if (css.isNotBlank()) { + val cssMin = css.replace(Regex("\\s*\n\\s*"), "") + append("var a=document.createElement('style');") + append("a.innerHTML='$cssMin';") + if (tag != null) { + append("a.id='$tag';") + } + append("document.head.appendChild(a);") + } + if (js.isNotBlank()) { + append(js) + } + } + var content = builder.append("}()").toString() + if (tag != null) { + content = singleInjector(tag, content) + } + return content + } + + private fun singleInjector(tag: String, content: String) = + """ + if (!window.hasOwnProperty("$tag") { + console.log("Registering $tag"); + window.$tag = true; + $content + } + """ + .trimIndent() +} diff --git a/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/injection/assets/CssActions.kt b/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/injection/assets/CssActions.kt new file mode 100644 index 000000000..448737e98 --- /dev/null +++ b/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/injection/assets/CssActions.kt @@ -0,0 +1,35 @@ +/* + * Copyright 2020 Allan Wang + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.pitchedapps.frost.webview.injection.assets + +import android.webkit.WebView +import com.pitchedapps.frost.webview.injection.JsBuilder +import com.pitchedapps.frost.webview.injection.JsInjector + +/** Small misc inline css assets */ +enum class CssActions(private val content: String) : JsInjector { + FullSizeImage( + "div._4prr[style*=\"max-width\"][style*=\"max-height\"]{max-width:none !important;max-height:none !important}", + ); + + private val injector: JsInjector = + JsBuilder().css(content).single("css-small-assets-$name").build() + + override fun inject(webView: WebView) { + injector.inject(webView) + } +} diff --git a/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/injection/assets/CssHider.kt b/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/injection/assets/CssHider.kt new file mode 100644 index 000000000..b191faea6 --- /dev/null +++ b/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/injection/assets/CssHider.kt @@ -0,0 +1,63 @@ +/* + * Copyright 2018 Allan Wang + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.pitchedapps.frost.webview.injection.assets + +import android.webkit.WebView +import com.pitchedapps.frost.webview.injection.JsBuilder +import com.pitchedapps.frost.webview.injection.JsInjector + +/** + * Created by Allan Wang on 2017-05-31. + * + * List of elements to hide + */ +enum class CssHider(private vararg val items: String) : JsInjector { + CORE("[data-sigil=m_login_upsell]", "[role=progressbar]"), + HEADER( + "#header:not(.mFuturePageHeader):not(.titled)", + "#mJewelNav", + "[data-sigil=MTopBlueBarHeader]", + "#header-notices", + "[data-sigil*=m-promo-jewel-header]", + ), + ADS("article[data-xt*=sponsor]", "article[data-store*=sponsor]", "article[data-ft*=sponsor]"), + PEOPLE_YOU_MAY_KNOW("article._d2r"), + SUGGESTED_GROUPS("article[data-ft*=\"ei\":]"), + + // Is it really this simple? + SUGGESTED_POSTS("article[data-store*=recommendation]", "article[data-ft*=recommendation]"), + COMPOSER("#MComposer"), + MESSENGER("._s15", "[data-testid=info_panel]", "js_i"), + NON_RECENT("article:not([data-store*=actor_name])"), + STORIES( + "#MStoriesTray", + // Sub element with just the tray; title is not a part of this + "[data-testid=story_tray]", + ), + POST_ACTIONS("footer [data-sigil=\"ufi-inline-actions\"]"), + POST_REACTIONS("footer [data-sigil=\"reactions-bling-bar\"]"); + + private val injector: JsInjector = + JsBuilder() + .css("${items.joinToString(separator = ",")}{display:none !important}") + .single("css-hider-$name") + .build() + + override fun inject(webView: WebView) { + injector.inject(webView) + } +} diff --git a/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/injection/assets/JsActions.kt b/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/injection/assets/JsActions.kt new file mode 100644 index 000000000..4bcde321c --- /dev/null +++ b/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/injection/assets/JsActions.kt @@ -0,0 +1,55 @@ +/* + * Copyright 2018 Allan Wang + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.pitchedapps.frost.webview.injection.assets + +import android.webkit.WebView +import com.pitchedapps.frost.facebook.FB_URL_BASE +import com.pitchedapps.frost.webview.injection.JsInjector + +/** + * Created by Allan Wang on 2017-05-31. + * + * Collection of short js functions that are embedded directly + */ +enum class JsActions(body: String) : JsInjector { + /** + * Redirects to login activity if create account is found see + * [com.pitchedapps.frost.web.FrostJSI.loadLogin] + */ + LOGIN_CHECK("document.getElementById('signup-button')&&Frost.loadLogin();"), + BASE_HREF("""document.write("");"""), + FETCH_BODY( + """setTimeout(function(){var e=document.querySelector("main");e||(e=document.querySelector("body")),Frost.handleHtml(e.outerHTML)},1e2);""", + ), + RETURN_BODY("return(document.getElementsByTagName('html')[0].innerHTML);"), + CREATE_POST(clickBySelector("#MComposer [onclick]")), + // CREATE_MSG(clickBySelector("a[rel=dialog]")), + /** Used as a pseudoinjector for maybe functions */ + EMPTY(""); + + val function = "(function(){$body})();" + + private val injector: JsInjector = JsInjector(function) + + override fun inject(webView: WebView) { + injector.inject(webView) + } +} + +@Suppress("NOTHING_TO_INLINE") +private inline fun clickBySelector(selector: String): String = + """document.querySelector("$selector").click()""" diff --git a/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/injection/assets/JsAssets.kt b/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/injection/assets/JsAssets.kt new file mode 100644 index 000000000..1515e4ab8 --- /dev/null +++ b/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/injection/assets/JsAssets.kt @@ -0,0 +1,77 @@ +/* + * Copyright 2018 Allan Wang + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.pitchedapps.frost.webview.injection.assets + +import android.content.Context +import android.webkit.WebView +import androidx.annotation.VisibleForTesting +import com.google.common.flogger.FluentLogger +import com.pitchedapps.frost.webview.injection.JsBuilder +import com.pitchedapps.frost.webview.injection.JsInjector +import java.io.BufferedReader +import java.io.FileNotFoundException +import java.util.Locale +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext + +/** + * Created by Allan Wang on 2017-05-31. Mapping of the available assets The enum name must match the + * js file name + */ +enum class JsAssets(private val singleLoad: Boolean = true) : JsInjector { + CLICK_A, + CONTEXT_A, + MEDIA, + HEADER_BADGES, + TEXTAREA_LISTENER, + NOTIF_MSG, + DOCUMENT_WATCHER, + HORIZONTAL_SCROLLING, + AUTO_RESIZE_TEXTAREA(singleLoad = false), + SCROLL_STOP, + ; + + @VisibleForTesting internal val file = "${name.lowercase(Locale.CANADA)}.js" + + private fun injectorBlocking(context: Context): JsInjector { + return try { + val content = + context.assets.open("frostcore/js/$file").bufferedReader().use(BufferedReader::readText) + JsBuilder().js(content).run { if (singleLoad) single(name) else this }.build() + } catch (e: FileNotFoundException) { + logger.atWarning().withCause(e).log("JsAssets file not found") + JsInjector.EMPTY + } + } + + private var injector: JsInjector = JsInjector.EMPTY + + override fun inject(webView: WebView) { + injector.inject(webView) + } + + private suspend fun load(context: Context) { + withContext(Dispatchers.IO) { injector = injectorBlocking(context) } + } + + companion object { + private val logger = FluentLogger.forEnclosingClass() + + suspend fun load(context: Context) = + withContext(Dispatchers.IO) { JsAssets.values().forEach { it.load(context) } } + } +} From f13b2d2998e03c91f71c84dd553e662deea67e97 Mon Sep 17 00:00:00 2001 From: Allan Wang Date: Tue, 20 Jun 2023 22:00:37 -0700 Subject: [PATCH 3/6] Remove gecko extension and add back old scripts --- .../webview/injection/FrostJsInjectors.kt | 4 +- .../webview/injection/assets/JsAssets.kt | 2 +- .../src/web/assets/frostcore/manifest.json | 48 ------ app-compose/src/web/package.json | 4 +- .../src/web/ts/auto_resize_textarea.ts | 43 ++++++ app-compose/src/web/ts/background/cookies.ts | 44 ------ app-compose/src/web/ts/click_a.ts | 18 +-- app-compose/src/web/ts/click_debugger.ts | 15 ++ app-compose/src/web/ts/context_a.ts | 145 ++++++++++++++++++ app-compose/src/web/ts/document_watcher.ts | 27 ++++ app-compose/src/web/ts/frost.ts | 21 --- app-compose/src/web/ts/header_badges.ts | 7 + .../src/web/ts/horizontal_scrolling.ts | 61 ++++++++ app-compose/src/web/ts/media.ts | 47 ++++++ app-compose/src/web/ts/notif_msg.ts | 25 +++ app-compose/src/web/ts/scroll_stop.ts | 25 +++ app-compose/src/web/ts/textarea_listener.ts | 31 ++++ app-compose/src/web/tsconfig.json | 2 +- app-compose/src/web/typings/browser.d.ts | 7 - app-compose/src/web/typings/frost.d.ts | 40 +++-- 20 files changed, 471 insertions(+), 145 deletions(-) delete mode 100644 app-compose/src/web/assets/frostcore/manifest.json create mode 100644 app-compose/src/web/ts/auto_resize_textarea.ts delete mode 100644 app-compose/src/web/ts/background/cookies.ts create mode 100644 app-compose/src/web/ts/click_debugger.ts create mode 100644 app-compose/src/web/ts/context_a.ts create mode 100644 app-compose/src/web/ts/document_watcher.ts delete mode 100644 app-compose/src/web/ts/frost.ts create mode 100644 app-compose/src/web/ts/header_badges.ts create mode 100644 app-compose/src/web/ts/horizontal_scrolling.ts create mode 100644 app-compose/src/web/ts/media.ts create mode 100644 app-compose/src/web/ts/notif_msg.ts create mode 100644 app-compose/src/web/ts/scroll_stop.ts create mode 100644 app-compose/src/web/ts/textarea_listener.ts delete mode 100644 app-compose/src/web/typings/browser.d.ts diff --git a/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/injection/FrostJsInjectors.kt b/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/injection/FrostJsInjectors.kt index 77cb6c59e..0af5774b8 100644 --- a/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/injection/FrostJsInjectors.kt +++ b/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/injection/FrostJsInjectors.kt @@ -42,11 +42,11 @@ internal constructor( theme.inject(view) } - fun getTheme(): JsInjector { + private fun getTheme(): JsInjector { return try { val content = context.assets - .open("frostcore/css/facebook/themes/material_glass.css") + .open("frost/css/facebook/themes/material_glass.css") .bufferedReader() .use(BufferedReader::readText) JsBuilder().css(content).build() diff --git a/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/injection/assets/JsAssets.kt b/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/injection/assets/JsAssets.kt index 1515e4ab8..a911bbbcc 100644 --- a/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/injection/assets/JsAssets.kt +++ b/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/injection/assets/JsAssets.kt @@ -50,7 +50,7 @@ enum class JsAssets(private val singleLoad: Boolean = true) : JsInjector { private fun injectorBlocking(context: Context): JsInjector { return try { val content = - context.assets.open("frostcore/js/$file").bufferedReader().use(BufferedReader::readText) + context.assets.open("frost/js/$file").bufferedReader().use(BufferedReader::readText) JsBuilder().js(content).run { if (singleLoad) single(name) else this }.build() } catch (e: FileNotFoundException) { logger.atWarning().withCause(e).log("JsAssets file not found") diff --git a/app-compose/src/web/assets/frostcore/manifest.json b/app-compose/src/web/assets/frostcore/manifest.json deleted file mode 100644 index 0f5bd27aa..000000000 --- a/app-compose/src/web/assets/frostcore/manifest.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - "manifest_version": 2, - "name": "frostcore", - "version": "1.0.0", - "description": "Core web extension for Frost", - "browser_specific_settings": { - "gecko": { - "id": "frost_gecko_core@pitchedapps" - } - }, - "background": { - "scripts": [ - "js/background/cookies.js" - ] - }, - "content_scripts": [ - { - "matches": [ - "" - ], - "js": [ - "js/frost.js" - ] - }, - { - "matches": [ - "*://*.facebook.com/*" - ], - "js": [ - "js/click_a.js" - ] - } - ], - "permissions": [ - "", - "activeTab", - "contextMenus", - "contextualIdentities", - "cookies", - "history", - "management", - "tabs", - "nativeMessaging", - "nativeMessagingFromContent", - "geckoViewAddons", - "webRequest" - ] -} \ No newline at end of file diff --git a/app-compose/src/web/package.json b/app-compose/src/web/package.json index 73ac90cfc..46f29bbc9 100644 --- a/app-compose/src/web/package.json +++ b/app-compose/src/web/package.json @@ -1,7 +1,7 @@ { "scripts": { - "compile": "tsc -p tsconfig.json && sass --no-source-map --style compressed --update scss:assets/frostcore/css", - "scss-watch": "sass --no-source-map --style compressed --update scss:assets/frostcore/css --watch" + "compile": "tsc -p tsconfig.json && sass --no-source-map --style compressed --update scss:assets/frost/css", + "scss-watch": "sass --no-source-map --style compressed --update scss:assets/frost/css --watch" }, "license": "MPL-2.0", "repository": { diff --git a/app-compose/src/web/ts/auto_resize_textarea.ts b/app-compose/src/web/ts/auto_resize_textarea.ts new file mode 100644 index 000000000..c71a75a1e --- /dev/null +++ b/app-compose/src/web/ts/auto_resize_textarea.ts @@ -0,0 +1,43 @@ +// Credits to https://codepen.io/tomhodgins/pen/KgazaE +(function () { + const classTag = 'frostAutoExpand'; + const textareas = >document.querySelectorAll(`textarea:not(.${classTag})`); + + const dataAttribute = 'data-frost-minHeight'; + + const _frostAutoExpand = (el: HTMLElement) => { + if (!el.hasAttribute(dataAttribute)) { + el.setAttribute(dataAttribute, el.offsetHeight.toString()); + } + // If no height is defined, have min bound to current height; + // otherwise we will allow for height decreases in case user deletes text + const minHeight = parseInt(el.getAttribute(dataAttribute) ?? '0'); + + // Save scroll position prior to height update + // See https://stackoverflow.com/a/18262927/4407321 + const scrollLeft = window.pageXOffset || + (document.documentElement || document.body.parentNode || document.body).scrollLeft; + const scrollTop = window.pageYOffset || + (document.documentElement || document.body.parentNode || document.body).scrollTop; + + el.style.height = 'inherit'; + el.style.height = `${Math.max(el.scrollHeight, minHeight)}px`; + + // Go to original scroll position + window.scrollTo(scrollLeft, scrollTop); + }; + function _frostExpandAll() { + textareas.forEach(_frostAutoExpand); + } + textareas.forEach(el => { + el.classList.add(classTag) + const __frostAutoExpand = () => { + _frostAutoExpand(el) + }; + el.addEventListener('paste', __frostAutoExpand) + el.addEventListener('input', __frostAutoExpand) + el.addEventListener('keyup', __frostAutoExpand) + }); + window.addEventListener('load', _frostExpandAll) + window.addEventListener('resize', _frostExpandAll) +}).call(undefined); diff --git a/app-compose/src/web/ts/background/cookies.ts b/app-compose/src/web/ts/background/cookies.ts deleted file mode 100644 index 5e1ee8b95..000000000 --- a/app-compose/src/web/ts/background/cookies.ts +++ /dev/null @@ -1,44 +0,0 @@ -async function updateCookies(changeInfo: browser.cookies._OnChangedChangeInfo) { - - const application = "frostBackgroundChannel" - - browser.runtime.sendNativeMessage(application, changeInfo) -} - -async function readCookies() { - const application = "frostBackgroundChannel" - - browser.runtime.sendNativeMessage(application, 'start cookie fetch') - - // Testing with domains or urls didn't work - const cookies = await browser.cookies.getAll({}); - - const cookies2 = await browser.cookies.getAll({ storeId: "firefox-container-frost-context-1" }) - - const cookieStores = await browser.cookies.getAllCookieStores(); - - browser.runtime.sendNativeMessage(application, { name: "cookies", data: cookies.length, stores: cookieStores.map((s) => s.id), data2: cookies2.length, data3: cookies.filter(s => s.storeId != 'firefox-default').length }) -} - -async function handleMessage(request: any, sender: browser.runtime.MessageSender, sendResponse: (response?: any) => void) { - browser.runtime.sendNativeMessage("frostBackgroundChannel", 'pre send') - - await new Promise(resolve => setTimeout(resolve, 1000)); - - browser.runtime.sendNativeMessage("frostBackgroundChannel", 'post send') - - sendResponse({ received: request, asdf: "asdf" }) -} - -// Reading cookies with storeId might not be fully supported on Android -// https://stackoverflow.com/q/76505000/4407321 -// Using manifest 3 stopped getAll from working -// Reading now always shows storeId as firefox-default -// Setting a cookie with a custom container does not seem to work - -// browser.cookies.onChanged.addListener(updateCookies); -// browser.tabs.onActivated.addListener(readCookies); -// browser.runtime.onStartup.addListener(readCookies); - -// browser.runtime.onMessage.addListener(handleMessage); - diff --git a/app-compose/src/web/ts/click_a.ts b/app-compose/src/web/ts/click_a.ts index ddf4e84c8..8a7cc48c6 100644 --- a/app-compose/src/web/ts/click_a.ts +++ b/app-compose/src/web/ts/click_a.ts @@ -1,7 +1,6 @@ -(async function () { +(function () { let prevented = false; - /** * Go up at most [depth] times, to retrieve a parent matching the provided predicate * If one is found, it is returned immediately. @@ -40,29 +39,29 @@ /** * Given event and target, return true if handled and false otherwise. */ - type EventHandler = (e: Event, target: HTMLElement) => Promise + type EventHandler = (e: Event, target: HTMLElement) => Boolean - const _frostGeneral: EventHandler = async (e, target) => { + const _frostGeneral: EventHandler = (e, target) => { // We now disable clicks for the main notification page if (document.getElementById("notifications_list")) { return false } const url = _parentUrl(target, 2); - return frost.loadUrl(url); + return Frost.loadUrl(url); }; - const _frostLaunchpadClick: EventHandler = async (e, target) => { + const _frostLaunchpadClick: EventHandler = (e, target) => { if (!_parentEl(target, 6, (el) => el.id === 'launchpad')) { return false } console.log('Clicked launchpad'); const url = _parentUrl(target, 5); - return frost.loadUrl(url); + return Frost.loadUrl(url); }; const handlers: EventHandler[] = [_frostLaunchpadClick, _frostGeneral]; - const _frostAClick = async (e: Event) => { + const _frostAClick = (e: Event) => { if (prevented) { console.log("Click intercept prevented"); return @@ -75,9 +74,8 @@ console.log("No element found"); return } - // TODO cannot use await here; copy logic over here for (const h of handlers) { - if (await h(e, target)) { + if (h(e, target)) { e.stopPropagation(); e.preventDefault(); return diff --git a/app-compose/src/web/ts/click_debugger.ts b/app-compose/src/web/ts/click_debugger.ts new file mode 100644 index 000000000..088271fa8 --- /dev/null +++ b/app-compose/src/web/ts/click_debugger.ts @@ -0,0 +1,15 @@ +// For desktop only + +(function () { + const _frostAContext = (e: Event) => { + // Commonality; check for valid target + const element = e.target || e.currentTarget || e.srcElement; + if (!(element instanceof Element)) { + console.log("No element found"); + return + } + console.log(`Clicked element ${element.tagName} ${element.className}`); + }; + + document.addEventListener('contextmenu', _frostAContext, true); +}).call(undefined); diff --git a/app-compose/src/web/ts/context_a.ts b/app-compose/src/web/ts/context_a.ts new file mode 100644 index 000000000..ad81279e6 --- /dev/null +++ b/app-compose/src/web/ts/context_a.ts @@ -0,0 +1,145 @@ +/** + * Context menu for links + * Largely mimics click_a.js + */ + +(function () { + let longClick = false; + + /** + * Go up at most [depth] times, to retrieve a parent matching the provided predicate + * If one is found, it is returned immediately. + * Otherwise, null is returned. + */ + function _parentEl(el: HTMLElement, depth: number, predicate: (el: HTMLElement) => boolean): HTMLElement | null { + for (let i = 0; i < depth + 1; i++) { + if (predicate(el)) { + return el + } + const parent = el.parentElement; + if (!parent) { + return null + } + el = parent + } + return null + } + + /** + * Given event and target, return true if handled and false otherwise. + */ + type EventHandler = (e: Event, target: HTMLElement) => Boolean + + const _frostCopyComment: EventHandler = (e, target) => { + if (!target.hasAttribute('data-commentid')) { + return false; + } + const text = target.innerText; + console.log(`Copy comment ${text}`); + Frost.contextMenu(null, text); + return true; + }; + + /** + * Posts should click a tag, with two parents up being div.story_body_container + */ + const _frostCopyPost: EventHandler = (e, target) => { + if (target.tagName !== 'A') { + return false; + } + const parent1 = target.parentElement; + if (!parent1 || parent1.tagName !== 'DIV') { + return false; + } + const parent2 = parent1.parentElement; + if (!parent2 || !parent2.classList.contains('story_body_container')) { + return false; + } + const url = target.getAttribute('href'); + const text = parent1.innerText; + console.log(`Copy post ${url} ${text}`); + Frost.contextMenu(url, text); + return true; + }; + + const _getImageStyleUrl = (el: Element): string | null => { + // Emojis and special characters may be images from a span + const img = el.querySelector("[style*=\"background-image: url(\"]:not(span)"); + if (!img) { + return null + } + return (window.getComputedStyle(img, null).backgroundImage).trim().slice(4, -1); + }; + + /** + * Opens image activity for posts with just one image + */ + const _frostImage: EventHandler = (e, target) => { + const element = _parentEl(target, 2, (el) => el.tagName === 'A'); + if (!element) { + return false; + } + const url = element.getAttribute('href'); + if (!url || url === '#') { + return false; + } + const text = (element.parentElement).innerText; + // Check if image item exists, first in children and then in parent + const imageUrl = _getImageStyleUrl(element) || _getImageStyleUrl(element.parentElement); + if (imageUrl) { + console.log(`Context image: ${imageUrl}`); + Frost.loadImage(imageUrl, text); + return true; + } + // Check if true img exists + const img = element.querySelector("img[src*=scontent]"); + if (img instanceof HTMLMediaElement) { + const imgUrl = img.src; + console.log(`Context img: ${imgUrl}`); + Frost.loadImage(imgUrl, text); + return true; + } + console.log(`Context content ${url} ${text}`); + Frost.contextMenu(url, text); + return true; + }; + + const handlers: EventHandler[] = [_frostImage, _frostCopyComment, _frostCopyPost]; + + const _frostAContext = (e: Event) => { + Frost.longClick(true); + longClick = true; + + /** + * Don't handle context events while scrolling + */ + if (Frost.isScrolling()) { + console.log("Skip from scrolling"); + return; + } + + /* + * Commonality; check for valid target + */ + const target = e.target || e.currentTarget || e.srcElement; + if (!(target instanceof HTMLElement)) { + console.log("No element found"); + return + } + for (const h of handlers) { + if (h(e, target)) { + e.stopPropagation(); + e.preventDefault(); + return + } + } + }; + + document.addEventListener('contextmenu', _frostAContext, true); + document.addEventListener('touchend', () => { + if (longClick) { + Frost.longClick(false); + longClick = false + } + }, true); +}).call(undefined); diff --git a/app-compose/src/web/ts/document_watcher.ts b/app-compose/src/web/ts/document_watcher.ts new file mode 100644 index 000000000..e671149c6 --- /dev/null +++ b/app-compose/src/web/ts/document_watcher.ts @@ -0,0 +1,27 @@ +// Emit key once half the viewport is covered +(function () { + const isReady = () => { + return document.body.scrollHeight > innerHeight + 100 + }; + + if (isReady()) { + console.log('Already ready'); + Frost.isReady(); + return + } + + console.log('Injected document watcher'); + + const observer = new MutationObserver(() => { + if (isReady()) { + observer.disconnect(); + Frost.isReady(); + console.log(`Documented surpassed height in ${performance.now()}`); + } + }); + + observer.observe(document, { + childList: true, + subtree: true + }) +}).call(undefined); diff --git a/app-compose/src/web/ts/frost.ts b/app-compose/src/web/ts/frost.ts deleted file mode 100644 index abf194f70..000000000 --- a/app-compose/src/web/ts/frost.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Mobile browsers don't support modules, so I'm creating a shared variable. - * - * No idea if this is good practice. - */ -const frost = (function () { - const application = "frostChannel" - - async function sendMessage(message: ExtensionModel): Promise { - return browser.runtime.sendNativeMessage(application, message) - } - - async function loadUrl(url: string | null): Promise { - if (url == null) return false - return sendMessage({ type: "url-click", url: url }) - } - - return { - sendMessage, loadUrl - } -}).call(undefined); \ No newline at end of file diff --git a/app-compose/src/web/ts/header_badges.ts b/app-compose/src/web/ts/header_badges.ts new file mode 100644 index 000000000..3f7588591 --- /dev/null +++ b/app-compose/src/web/ts/header_badges.ts @@ -0,0 +1,7 @@ +// Fetches the header contents if it exists +(function() { + const header = document.getElementById('header'); + if (header) { + Frost.handleHeader(header.outerHTML); + } +}).call(undefined); diff --git a/app-compose/src/web/ts/horizontal_scrolling.ts b/app-compose/src/web/ts/horizontal_scrolling.ts new file mode 100644 index 000000000..b104725eb --- /dev/null +++ b/app-compose/src/web/ts/horizontal_scrolling.ts @@ -0,0 +1,61 @@ +(function () { + + /** + * Go up at most [depth] times, to retrieve a parent matching the provided predicate + * If one is found, it is returned immediately. + * Otherwise, null is returned. + */ + function _parentEl(el: HTMLElement, depth: number, predicate: (el: HTMLElement) => boolean): HTMLElement | null { + for (let i = 0; i < depth + 1; i++) { + if (predicate(el)) { + return el + } + const parent = el.parentElement; + if (!parent) { + return null + } + el = parent + } + return null + } + + /** + * Check if element can scroll horizontally. + * We primarily rely on the overflow-x field. + * For performance reasons, we will check scrollWidth first to see if scrolling is a possibility + */ + function _canScrollHorizontally(el: HTMLElement): boolean { + /* + * Sometimes the offsetWidth is off by < 10px. We use the multiplier + * since the trays are typically more than 2 times greater + */ + if (el.scrollWidth > el.offsetWidth * 1.2) { + return true + } + const styles = window.getComputedStyle(el); + /* + * Works well in testing, but on mobile it just shows 'visible' + */ + return styles.overflowX === 'scroll'; + } + + const _frostCheckHorizontalScrolling = (e: Event) => { + const target = e.target || e.currentTarget || e.srcElement; + if (!(target instanceof HTMLElement)) { + return + } + const scrollable = _parentEl(target, 5, _canScrollHorizontally) !== null; + if (scrollable) { + console.log('Pause horizontal scrolling'); + Frost.allowHorizontalScrolling(false); + } + }; + + const _frostResetHorizontalScrolling = (e: Event) => { + Frost.allowHorizontalScrolling(true) + }; + + document.addEventListener('touchstart', _frostCheckHorizontalScrolling, true); + document.addEventListener('touchend', _frostResetHorizontalScrolling, true); +}).call(undefined); + diff --git a/app-compose/src/web/ts/media.ts b/app-compose/src/web/ts/media.ts new file mode 100644 index 000000000..5b9b1a54b --- /dev/null +++ b/app-compose/src/web/ts/media.ts @@ -0,0 +1,47 @@ +// Handles media events +(function () { + const _frostMediaClick = (e: Event) => { + const target = e.target || e.srcElement; + if (!(target instanceof HTMLElement)) { + return + } + let element: HTMLElement = target; + const dataset = element.dataset; + if (!dataset || !dataset.sigil || dataset.sigil.toLowerCase().indexOf('inlinevideo') == -1) { + return + } + let i = 0; + while (!element.hasAttribute('data-store')) { + if (++i > 2) { + return + } + element = element.parentNode; + } + const store = element.dataset.store; + if (!store) { + return + } + + let dataStore; + + try { + dataStore = JSON.parse(store) + } catch (e) { + return + } + + const url = dataStore.src; + + // !startsWith; see https://stackoverflow.com/a/36876507/4407321 + if (!url || url.lastIndexOf('http', 0) !== 0) { + return + } + + console.log(`Inline video ${url}`); + if (Frost.loadVideo(url, dataStore.animatedGifVideo || false)) { + e.stopPropagation() + } + }; + + document.addEventListener('click', _frostMediaClick, true); +}).call(undefined); diff --git a/app-compose/src/web/ts/notif_msg.ts b/app-compose/src/web/ts/notif_msg.ts new file mode 100644 index 000000000..b7ce7a191 --- /dev/null +++ b/app-compose/src/web/ts/notif_msg.ts @@ -0,0 +1,25 @@ +// Binds callback to an invisible webview to take in the search events +(function () { + let finished = false; + const x = new MutationObserver(() => { + const _f_thread = document.querySelector('#threadlist_rows'); + if (!_f_thread) { + return + } + console.log(`Found message threads ${_f_thread.outerHTML}`); + Frost.handleHtml(_f_thread.outerHTML); + finished = true; + x.disconnect(); + }); + x.observe(document, { + childList: true, + subtree: true + }); + setTimeout(() => { + if (!finished) { + finished = true; + console.log('Message thread timeout cancellation'); + Frost.handleHtml("") + } + }, 20000); +}).call(undefined); diff --git a/app-compose/src/web/ts/scroll_stop.ts b/app-compose/src/web/ts/scroll_stop.ts new file mode 100644 index 000000000..1ec6d30bc --- /dev/null +++ b/app-compose/src/web/ts/scroll_stop.ts @@ -0,0 +1,25 @@ +// Listen when scrolling events stop +(function () { + let scrollTimeout: number | undefined = undefined; + let scrolling: boolean = false; + + window.addEventListener('scroll', function (event) { + + if (!scrolling) { + Frost.setScrolling(true); + scrolling = true; + } + + window.clearTimeout(scrollTimeout); + + scrollTimeout = setTimeout(function () { + if (scrolling) { + Frost.setScrolling(false); + scrolling = false; + } + }, 600); + // For our specific use case, we want to release other features pretty far after scrolling stops + // For general scrolling use cases, the delta can be much smaller + // My assumption for context menus is that the long press is 500ms + }, false); +}).call(undefined); \ No newline at end of file diff --git a/app-compose/src/web/ts/textarea_listener.ts b/app-compose/src/web/ts/textarea_listener.ts new file mode 100644 index 000000000..062f5bf67 --- /dev/null +++ b/app-compose/src/web/ts/textarea_listener.ts @@ -0,0 +1,31 @@ +/* + * focus listener for textareas + * since swipe to refresh is quite sensitive, we will disable it + * when we detect a user typing + * note that this extends passed having a keyboard opened, + * as a user may still be reviewing his/her post + * swiping should automatically be reset on refresh + */ +(function () { + const _frostFocus = (e: Event) => { + const element = e.target || e.srcElement; + if (!(element instanceof Element)) { + return + } + console.log(`FrostJSI focus, ${element.tagName}`); + if (element.tagName === 'TEXTAREA') { + Frost.disableSwipeRefresh(true); + } + }; + + const _frostBlur = (e: Event) => { + const element = e.target || e.srcElement; + if (!(element instanceof Element)) { + return + } + console.log(`FrostJSI blur, ${element.tagName}`); + Frost.disableSwipeRefresh(false); + }; + document.addEventListener("focus", _frostFocus, true); + document.addEventListener("blur", _frostBlur, true); +}).call(undefined); diff --git a/app-compose/src/web/tsconfig.json b/app-compose/src/web/tsconfig.json index 64032f84f..2bf985f60 100644 --- a/app-compose/src/web/tsconfig.json +++ b/app-compose/src/web/tsconfig.json @@ -16,7 +16,7 @@ "allowUnreachableCode": true, "allowUnusedLabels": true, "removeComments": true, - "outDir": "assets/frostcore/js" + "outDir": "assets/frost/js" }, "include": [ "ts", diff --git a/app-compose/src/web/typings/browser.d.ts b/app-compose/src/web/typings/browser.d.ts deleted file mode 100644 index f631123a4..000000000 --- a/app-compose/src/web/typings/browser.d.ts +++ /dev/null @@ -1,7 +0,0 @@ -declare namespace browser.runtime { - interface Port { - postMessage: (message: string) => void; - postMessage: (message: ExtensionModel) => void; - } - function sendNativeMessage(application: string, message: ExtensionModel): Promise; -} \ No newline at end of file diff --git a/app-compose/src/web/typings/frost.d.ts b/app-compose/src/web/typings/frost.d.ts index 1c4538517..9f77ce9e1 100644 --- a/app-compose/src/web/typings/frost.d.ts +++ b/app-compose/src/web/typings/frost.d.ts @@ -1,11 +1,33 @@ -type TestModel = { - type: 'test-model' - message: string +declare interface FrostJSI { + loadUrl(url: string | null): boolean + + loadVideo(url: string | null, isGif: boolean): boolean + + reloadBaseUrl(animate: boolean) + + contextMenu(url: string | null, text: string | null) + + longClick(start: boolean) + + disableSwipeRefresh(disable: boolean) + + loadLogin() + + loadImage(imageUrl: string, text: string | null) + + emit(flag: number) + + isReady() + + handleHtml(html: string | null) + + handleHeader(html: string | null) + + allowHorizontalScrolling(enable: boolean) + + setScrolling(scrolling: boolean) + + isScrolling(): boolean } -type UrlClickModel = { - type: 'url-click' - url: string -} - -type ExtensionModel = TestModel | UrlClickModel +declare var Frost: FrostJSI; From 37f53e35f28ae2ac9f88e96ffea11a029b825bc0 Mon Sep 17 00:00:00 2001 From: Allan Wang Date: Tue, 20 Jun 2023 23:21:27 -0700 Subject: [PATCH 4/6] Fix theming --- .../pitchedapps/frost/compose/FrostTheme.kt | 12 ++++- .../frost/compose/webview/FrostWebCompose.kt | 21 +------- .../com/pitchedapps/frost/facebook/FbConst.kt | 2 +- .../com/pitchedapps/frost/hilt/FrostModule.kt | 5 +- .../pitchedapps/frost/main/MainActivity.kt | 3 ++ .../frost/main/MainScreenWebView.kt | 2 + .../pitchedapps/frost/view/FrostWebView.kt | 54 +++++++++++++++++++ .../pitchedapps/frost/view/NestedWebView.kt | 16 +++--- .../frost/webview/FrostWebViewClients.kt | 34 ++++++++---- .../webview/injection/FrostJsInjectors.kt | 9 ++-- .../frost/webview/injection/JsInjector.kt | 9 ++-- .../webview/injection/assets/JsAssets.kt | 4 ++ 12 files changed, 118 insertions(+), 53 deletions(-) create mode 100644 app-compose/src/main/kotlin/com/pitchedapps/frost/view/FrostWebView.kt diff --git a/app-compose/src/main/kotlin/com/pitchedapps/frost/compose/FrostTheme.kt b/app-compose/src/main/kotlin/com/pitchedapps/frost/compose/FrostTheme.kt index 4b7ccab9d..216457092 100644 --- a/app-compose/src/main/kotlin/com/pitchedapps/frost/compose/FrostTheme.kt +++ b/app-compose/src/main/kotlin/com/pitchedapps/frost/compose/FrostTheme.kt @@ -27,6 +27,7 @@ import androidx.compose.material3.lightColorScheme import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext /** Main Frost compose theme. */ @@ -34,13 +35,14 @@ import androidx.compose.ui.platform.LocalContext fun FrostTheme( isDarkTheme: Boolean = isSystemInDarkTheme(), isDynamicColor: Boolean = true, + transparent: Boolean = true, modifier: Modifier = Modifier, content: @Composable () -> Unit ) { val context = LocalContext.current val dynamicColor = isDynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S val colorScheme = - remember(dynamicColor, isDarkTheme) { + remember(dynamicColor, isDarkTheme, transparent) { when { dynamicColor && isDarkTheme -> { dynamicDarkColorScheme(context) @@ -53,5 +55,11 @@ fun FrostTheme( } } - MaterialTheme(colorScheme = colorScheme) { Surface(modifier = modifier, content = content) } + MaterialTheme(colorScheme = colorScheme) { + Surface( + modifier = modifier, + color = if (transparent) Color.Transparent else MaterialTheme.colorScheme.surface, + content = content, + ) + } } diff --git a/app-compose/src/main/kotlin/com/pitchedapps/frost/compose/webview/FrostWebCompose.kt b/app-compose/src/main/kotlin/com/pitchedapps/frost/compose/webview/FrostWebCompose.kt index 4af7739d3..958b18862 100644 --- a/app-compose/src/main/kotlin/com/pitchedapps/frost/compose/webview/FrostWebCompose.kt +++ b/app-compose/src/main/kotlin/com/pitchedapps/frost/compose/webview/FrostWebCompose.kt @@ -31,7 +31,7 @@ import androidx.compose.ui.viewinterop.AndroidView import androidx.core.widget.NestedScrollView import com.google.common.flogger.FluentLogger import com.pitchedapps.frost.ext.WebTargetId -import com.pitchedapps.frost.view.NestedWebView +import com.pitchedapps.frost.view.FrostWebView import com.pitchedapps.frost.web.state.FrostWebStore import com.pitchedapps.frost.web.state.TabAction import com.pitchedapps.frost.web.state.TabAction.ResponseAction.LoadUrlResponseAction @@ -126,7 +126,7 @@ class FrostWebCompose( AndroidView( factory = { context -> val childView = - NestedWebView(context) + FrostWebView(context) .apply { onCreated(this) @@ -150,9 +150,6 @@ class FrostWebCompose( } .also { webView = it } - // Workaround a crash on certain devices that expect WebView to be - // wrapped in a ViewGroup. - // b/243567497 val parentLayout = NestedScrollView(context) parentLayout.layoutParams = FrameLayout.LayoutParams( @@ -176,17 +173,3 @@ class FrostWebCompose( private val logger = FluentLogger.forEnclosingClass() } } - - -// override fun onReceivedError( -// view: WebView, -// request: WebResourceRequest?, -// error: WebResourceError? -// ) { -// super.onReceivedError(view, request, error) -// -// if (error != null) { -// state.errorsForCurrentRequest.add(WebViewError(request, error)) -// } -// } -// } diff --git a/app-compose/src/main/kotlin/com/pitchedapps/frost/facebook/FbConst.kt b/app-compose/src/main/kotlin/com/pitchedapps/frost/facebook/FbConst.kt index 4d371f36e..8247397f5 100644 --- a/app-compose/src/main/kotlin/com/pitchedapps/frost/facebook/FbConst.kt +++ b/app-compose/src/main/kotlin/com/pitchedapps/frost/facebook/FbConst.kt @@ -24,7 +24,7 @@ const val WWW_FACEBOOK_COM = "www.$FACEBOOK_COM" const val WWW_MESSENGER_COM = "www.$MESSENGER_COM" const val HTTPS_FACEBOOK_COM = "https://$WWW_FACEBOOK_COM" const val HTTPS_MESSENGER_COM = "https://$WWW_MESSENGER_COM" -const val FACEBOOK_BASE_COM = "m.$FACEBOOK_COM" +const val FACEBOOK_BASE_COM = "touch.$FACEBOOK_COM" const val FB_URL_BASE = "https://$FACEBOOK_BASE_COM/" const val FACEBOOK_MBASIC_COM = "mbasic.$FACEBOOK_COM" const val FB_URL_MBASIC_BASE = "https://$FACEBOOK_MBASIC_COM/" diff --git a/app-compose/src/main/kotlin/com/pitchedapps/frost/hilt/FrostModule.kt b/app-compose/src/main/kotlin/com/pitchedapps/frost/hilt/FrostModule.kt index 3a3e5af1a..6df9f6cc7 100644 --- a/app-compose/src/main/kotlin/com/pitchedapps/frost/hilt/FrostModule.kt +++ b/app-compose/src/main/kotlin/com/pitchedapps/frost/hilt/FrostModule.kt @@ -24,7 +24,6 @@ import dagger.Provides import dagger.hilt.InstallIn import dagger.hilt.components.SingletonComponent import javax.inject.Qualifier -import javax.inject.Singleton @Qualifier annotation class Frost @@ -54,7 +53,7 @@ object FrostModule { "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/112.0" private const val USER_AGENT_WINDOWS_FROST = - "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.90 Safari/537.36" + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.70 Safari/537.36" - @Provides @Singleton @Frost fun userAgent(): String = USER_AGENT_WINDOWS_FROST + @Provides @Frost fun userAgent(): String = USER_AGENT_WINDOWS_FROST } diff --git a/app-compose/src/main/kotlin/com/pitchedapps/frost/main/MainActivity.kt b/app-compose/src/main/kotlin/com/pitchedapps/frost/main/MainActivity.kt index 23192ccfc..a64f6053d 100644 --- a/app-compose/src/main/kotlin/com/pitchedapps/frost/main/MainActivity.kt +++ b/app-compose/src/main/kotlin/com/pitchedapps/frost/main/MainActivity.kt @@ -21,6 +21,7 @@ import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.core.view.WindowCompat import com.google.common.flogger.FluentLogger +import com.pitchedapps.frost.R import com.pitchedapps.frost.compose.FrostTheme import com.pitchedapps.frost.web.state.FrostWebStore import dagger.hilt.android.AndroidEntryPoint @@ -38,6 +39,8 @@ class MainActivity : ComponentActivity() { @Inject lateinit var store: FrostWebStore override fun onCreate(savedInstanceState: Bundle?) { + // TODO make configurable + setTheme(R.style.FrostTheme_Transparent) super.onCreate(savedInstanceState) logger.atInfo().log("onCreate main activity") diff --git a/app-compose/src/main/kotlin/com/pitchedapps/frost/main/MainScreenWebView.kt b/app-compose/src/main/kotlin/com/pitchedapps/frost/main/MainScreenWebView.kt index c9da5835c..b148c0c7b 100644 --- a/app-compose/src/main/kotlin/com/pitchedapps/frost/main/MainScreenWebView.kt +++ b/app-compose/src/main/kotlin/com/pitchedapps/frost/main/MainScreenWebView.kt @@ -41,6 +41,7 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.lifecycle.viewmodel.compose.viewModel import com.pitchedapps.frost.compose.webview.FrostWebCompose import com.pitchedapps.frost.ext.WebTargetId @@ -59,6 +60,7 @@ fun MainScreenWebView(modifier: Modifier = Modifier, homeTabs: List Scaffold( modifier = modifier, + containerColor = Color.Transparent, topBar = { MainTopBar(modifier = modifier) }, bottomBar = { MainBottomBar( diff --git a/app-compose/src/main/kotlin/com/pitchedapps/frost/view/FrostWebView.kt b/app-compose/src/main/kotlin/com/pitchedapps/frost/view/FrostWebView.kt new file mode 100644 index 000000000..d90b669ab --- /dev/null +++ b/app-compose/src/main/kotlin/com/pitchedapps/frost/view/FrostWebView.kt @@ -0,0 +1,54 @@ +/* + * Copyright 2023 Allan Wang + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.pitchedapps.frost.view + +import android.content.Context +import android.graphics.Color +import android.util.AttributeSet +import com.pitchedapps.frost.hilt.Frost +import dagger.hilt.android.AndroidEntryPoint +import java.util.Optional +import javax.inject.Inject + +@AndroidEntryPoint +class FrostWebView +@JvmOverloads +constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : + NestedWebView(context, attrs, defStyleAttr) { + + @Inject @Frost lateinit var userAgent: Optional + + init { + userAgent.ifPresent { + settings.userAgentString = it + println("Set user agent to $it") + } + with(settings) { + // noinspection SetJavaScriptEnabled + javaScriptEnabled = true + mediaPlaybackRequiresUserGesture = false // TODO check if we need this + allowFileAccess = true + // textZoom + domStorageEnabled = true + + setLayerType(LAYER_TYPE_HARDWARE, null) + setBackgroundColor(Color.TRANSPARENT) + // Download listener + // JS Interface + } + } +} diff --git a/app-compose/src/main/kotlin/com/pitchedapps/frost/view/NestedWebView.kt b/app-compose/src/main/kotlin/com/pitchedapps/frost/view/NestedWebView.kt index dbc955871..6d160ec5f 100644 --- a/app-compose/src/main/kotlin/com/pitchedapps/frost/view/NestedWebView.kt +++ b/app-compose/src/main/kotlin/com/pitchedapps/frost/view/NestedWebView.kt @@ -30,7 +30,7 @@ import androidx.core.view.ViewCompat * * Webview extension that handles nested scrolls */ -class NestedWebView(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : +open class NestedWebView(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : WebView(context, attrs, defStyleAttr), NestedScrollingChild3 { // No JvmOverloads due to hilt @@ -109,21 +109,17 @@ class NestedWebView(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : override fun isNestedScrollingEnabled() = childHelper.isNestedScrollingEnabled - override fun startNestedScroll(axes: Int, type: Int): Boolean { - TODO("not implemented") - } + override fun startNestedScroll(axes: Int, type: Int): Boolean = + childHelper.startNestedScroll(axes, type) override fun startNestedScroll(axes: Int) = childHelper.startNestedScroll(axes) - override fun stopNestedScroll(type: Int) { - TODO("not implemented") - } + override fun stopNestedScroll(type: Int) = childHelper.stopNestedScroll(type) override fun stopNestedScroll() = childHelper.stopNestedScroll() - override fun hasNestedScrollingParent(type: Int): Boolean { - TODO("not implemented") - } + override fun hasNestedScrollingParent(type: Int): Boolean = + childHelper.hasNestedScrollingParent(type) override fun hasNestedScrollingParent() = childHelper.hasNestedScrollingParent() diff --git a/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/FrostWebViewClients.kt b/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/FrostWebViewClients.kt index a9bb3bc88..0b89c7556 100644 --- a/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/FrostWebViewClients.kt +++ b/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/FrostWebViewClients.kt @@ -17,6 +17,7 @@ package com.pitchedapps.frost.webview import android.graphics.Bitmap +import android.webkit.WebResourceError import android.webkit.WebResourceRequest import android.webkit.WebResourceResponse import android.webkit.WebView @@ -26,6 +27,7 @@ import com.pitchedapps.frost.ext.WebTargetId import com.pitchedapps.frost.facebook.FACEBOOK_BASE_COM import com.pitchedapps.frost.facebook.WWW_FACEBOOK_COM import com.pitchedapps.frost.facebook.isExplicitIntent +import com.pitchedapps.frost.facebook.isFacebookUrl import com.pitchedapps.frost.web.FrostWebHelper import com.pitchedapps.frost.web.state.FrostWebStore import com.pitchedapps.frost.web.state.TabAction @@ -132,21 +134,23 @@ class FrostWebViewClient( // refresh.offer(true) } - // private fun injectBackgroundColor() { - // web?.setBackgroundColor( - // when { - // isMain -> Color.TRANSPARENT - // web.url.isFacebookUrl -> themeProvider.bgColor.withAlpha(255) - // else -> Color.WHITE - // } - // ) - // } + // private fun WebView.injectBackgroundColor(url: String?) { + // setBackgroundColor( + // when { + // isMain -> Color.TRANSPARENT + // url.isFacebookUrl -> themeProvider.bgColor.withAlpha(255) + // else -> Color.WHITE + // } + // ) + // } override fun onPageCommitVisible(view: WebView, url: String?) { super.onPageCommitVisible(view, url) - frostJsInjectors.injectOnPageCommitVisible(view, url) + + when { + url.isFacebookUrl -> frostJsInjectors.facebookInjectOnPageCommitVisible(view, url) + } } - // injectBackgroundColor() // when { // url.isFacebookUrl -> { // v { "FB Page commit visible" } @@ -251,6 +255,14 @@ class FrostWebViewClient( return super.shouldOverrideUrlLoading(view, request) } + override fun onReceivedError( + view: WebView?, + request: WebResourceRequest?, + error: WebResourceError? + ) { + super.onReceivedError(view, request, error) + } + companion object { private val logger = FluentLogger.forEnclosingClass() } diff --git a/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/injection/FrostJsInjectors.kt b/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/injection/FrostJsInjectors.kt index 0af5774b8..c19f4ae39 100644 --- a/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/injection/FrostJsInjectors.kt +++ b/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/injection/FrostJsInjectors.kt @@ -20,6 +20,8 @@ import android.content.Context import android.webkit.WebView import com.google.common.flogger.FluentLogger import com.pitchedapps.frost.webview.injection.assets.JsActions +import com.pitchedapps.frost.webview.injection.assets.JsAssets +import com.pitchedapps.frost.webview.injection.assets.inject import dagger.hilt.android.qualifiers.ApplicationContext import java.io.BufferedReader import java.io.FileNotFoundException @@ -37,9 +39,9 @@ internal constructor( @Volatile private var theme: JsInjector = JsInjector.EMPTY - fun injectOnPageCommitVisible(view: WebView, url: String?) { + fun facebookInjectOnPageCommitVisible(view: WebView, url: String?) { logger.atInfo().log("inject page commit visible %b", theme != JsInjector.EMPTY) - theme.inject(view) + listOf(theme, JsAssets.CLICK_A).inject(view) } private fun getTheme(): JsInjector { @@ -49,7 +51,8 @@ internal constructor( .open("frost/css/facebook/themes/material_glass.css") .bufferedReader() .use(BufferedReader::readText) - JsBuilder().css(content).build() + logger.atInfo().log("css %s", content) + JsBuilder().css(content).single("material_glass").build() } catch (e: FileNotFoundException) { logger.atSevere().withCause(e).log("CssAssets file not found") JsActions.EMPTY diff --git a/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/injection/JsInjector.kt b/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/injection/JsInjector.kt index 28211e26d..9fa410974 100644 --- a/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/injection/JsInjector.kt +++ b/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/injection/JsInjector.kt @@ -75,7 +75,6 @@ class JsBuilder { val tag = this.tag val builder = StringBuilder().apply { - append("!function(){") if (css.isNotBlank()) { val cssMin = css.replace(Regex("\\s*\n\\s*"), "") append("var a=document.createElement('style');") @@ -89,16 +88,18 @@ class JsBuilder { append(js) } } - var content = builder.append("}()").toString() + var content = builder.toString() if (tag != null) { content = singleInjector(tag, content) } - return content + return wrapAnonymous(content) } + private fun wrapAnonymous(body: String) = "(function(){$body})();" + private fun singleInjector(tag: String, content: String) = """ - if (!window.hasOwnProperty("$tag") { + if (!window.hasOwnProperty("$tag")) { console.log("Registering $tag"); window.$tag = true; $content diff --git a/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/injection/assets/JsAssets.kt b/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/injection/assets/JsAssets.kt index a911bbbcc..a4ad2e432 100644 --- a/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/injection/assets/JsAssets.kt +++ b/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/injection/assets/JsAssets.kt @@ -75,3 +75,7 @@ enum class JsAssets(private val singleLoad: Boolean = true) : JsInjector { withContext(Dispatchers.IO) { JsAssets.values().forEach { it.load(context) } } } } + +fun List.inject(webView: WebView) { + forEach { it.inject(webView) } +} From 36a38deb10f83ee4edfbd0b6acb43a027714d05e Mon Sep 17 00:00:00 2001 From: Allan Wang Date: Tue, 20 Jun 2023 23:28:24 -0700 Subject: [PATCH 5/6] Move components --- .../src/main/kotlin/com/pitchedapps/frost/FrostApp.kt | 2 +- .../frost/{hilt => components}/FrostComponents.kt | 5 +---- .../kotlin/com/pitchedapps/frost/components/UseCases.kt | 2 +- .../src/main/kotlin/com/pitchedapps/frost/ext/ContextExt.kt | 2 +- .../com/pitchedapps/frost/main/MainScreenViewModel.kt | 2 +- .../com/pitchedapps/frost/web/state/FrostWebAction.kt | 2 +- .../pitchedapps/frost/web/state/reducer/TabListReducer.kt | 6 +++++- .../frost/{components => web}/usecases/HomeTabsUseCases.kt | 2 +- 8 files changed, 12 insertions(+), 11 deletions(-) rename app-compose/src/main/kotlin/com/pitchedapps/frost/{hilt => components}/FrostComponents.kt (85%) rename app-compose/src/main/kotlin/com/pitchedapps/frost/{components => web}/usecases/HomeTabsUseCases.kt (96%) diff --git a/app-compose/src/main/kotlin/com/pitchedapps/frost/FrostApp.kt b/app-compose/src/main/kotlin/com/pitchedapps/frost/FrostApp.kt index 9a1c4ebcd..50e808b1f 100644 --- a/app-compose/src/main/kotlin/com/pitchedapps/frost/FrostApp.kt +++ b/app-compose/src/main/kotlin/com/pitchedapps/frost/FrostApp.kt @@ -20,7 +20,7 @@ import android.app.Activity import android.app.Application import android.os.Bundle import com.google.common.flogger.FluentLogger -import com.pitchedapps.frost.hilt.FrostComponents +import com.pitchedapps.frost.components.FrostComponents import com.pitchedapps.frost.webview.injection.FrostJsInjectors import com.pitchedapps.frost.webview.injection.assets.JsAssets import dagger.hilt.android.HiltAndroidApp diff --git a/app-compose/src/main/kotlin/com/pitchedapps/frost/hilt/FrostComponents.kt b/app-compose/src/main/kotlin/com/pitchedapps/frost/components/FrostComponents.kt similarity index 85% rename from app-compose/src/main/kotlin/com/pitchedapps/frost/hilt/FrostComponents.kt rename to app-compose/src/main/kotlin/com/pitchedapps/frost/components/FrostComponents.kt index a6e544f34..4563a39e9 100644 --- a/app-compose/src/main/kotlin/com/pitchedapps/frost/hilt/FrostComponents.kt +++ b/app-compose/src/main/kotlin/com/pitchedapps/frost/components/FrostComponents.kt @@ -14,11 +14,8 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package com.pitchedapps.frost.hilt +package com.pitchedapps.frost.components -import com.pitchedapps.frost.components.Core -import com.pitchedapps.frost.components.FrostDataStore -import com.pitchedapps.frost.components.UseCases import javax.inject.Inject import javax.inject.Singleton diff --git a/app-compose/src/main/kotlin/com/pitchedapps/frost/components/UseCases.kt b/app-compose/src/main/kotlin/com/pitchedapps/frost/components/UseCases.kt index 9eeab85e6..6342c0c1b 100644 --- a/app-compose/src/main/kotlin/com/pitchedapps/frost/components/UseCases.kt +++ b/app-compose/src/main/kotlin/com/pitchedapps/frost/components/UseCases.kt @@ -16,7 +16,7 @@ */ package com.pitchedapps.frost.components -import com.pitchedapps.frost.components.usecases.HomeTabsUseCases +import com.pitchedapps.frost.web.usecases.HomeTabsUseCases import javax.inject.Inject import javax.inject.Singleton diff --git a/app-compose/src/main/kotlin/com/pitchedapps/frost/ext/ContextExt.kt b/app-compose/src/main/kotlin/com/pitchedapps/frost/ext/ContextExt.kt index 2a4a5f9e5..7e021ac3c 100644 --- a/app-compose/src/main/kotlin/com/pitchedapps/frost/ext/ContextExt.kt +++ b/app-compose/src/main/kotlin/com/pitchedapps/frost/ext/ContextExt.kt @@ -21,7 +21,7 @@ import android.content.Context import android.content.Intent import android.os.Bundle import com.pitchedapps.frost.FrostApp -import com.pitchedapps.frost.hilt.FrostComponents +import com.pitchedapps.frost.components.FrostComponents /** Launches new activities. */ inline fun Context.launchActivity( diff --git a/app-compose/src/main/kotlin/com/pitchedapps/frost/main/MainScreenViewModel.kt b/app-compose/src/main/kotlin/com/pitchedapps/frost/main/MainScreenViewModel.kt index ca50fe44d..6a2ecb347 100644 --- a/app-compose/src/main/kotlin/com/pitchedapps/frost/main/MainScreenViewModel.kt +++ b/app-compose/src/main/kotlin/com/pitchedapps/frost/main/MainScreenViewModel.kt @@ -21,10 +21,10 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import androidx.lifecycle.ViewModel +import com.pitchedapps.frost.components.FrostComponents import com.pitchedapps.frost.ext.GeckoContextId import com.pitchedapps.frost.ext.idData import com.pitchedapps.frost.ext.toContextId -import com.pitchedapps.frost.hilt.FrostComponents import com.pitchedapps.frost.web.state.FrostWebStore import com.pitchedapps.frost.webview.FrostWebComposer import dagger.hilt.android.lifecycle.HiltViewModel diff --git a/app-compose/src/main/kotlin/com/pitchedapps/frost/web/state/FrostWebAction.kt b/app-compose/src/main/kotlin/com/pitchedapps/frost/web/state/FrostWebAction.kt index 44af46de9..613ec7822 100644 --- a/app-compose/src/main/kotlin/com/pitchedapps/frost/web/state/FrostWebAction.kt +++ b/app-compose/src/main/kotlin/com/pitchedapps/frost/web/state/FrostWebAction.kt @@ -38,7 +38,7 @@ object InitAction : FrostWebAction /** Actions affecting multiple tabs */ sealed interface TabListAction : FrostWebAction { - data class SetHomeTabs(val data: List) : TabListAction + data class SetHomeTabs(val data: List, val selectedTab: Int? = 0) : TabListAction data class SelectHomeTab(val id: WebTargetId) : TabListAction } diff --git a/app-compose/src/main/kotlin/com/pitchedapps/frost/web/state/reducer/TabListReducer.kt b/app-compose/src/main/kotlin/com/pitchedapps/frost/web/state/reducer/TabListReducer.kt index 6ee0d6332..a1a1eda0c 100644 --- a/app-compose/src/main/kotlin/com/pitchedapps/frost/web/state/reducer/TabListReducer.kt +++ b/app-compose/src/main/kotlin/com/pitchedapps/frost/web/state/reducer/TabListReducer.kt @@ -38,7 +38,11 @@ internal constructor( is SetHomeTabs -> { val tabs = action.data.mapIndexed { i, fbItem -> fbItem.toHomeTabSession(context, i, state.auth) } - state.copy(homeTabs = tabs) + val selectedTab = action.selectedTab?.let { HomeTabSessionState.homeTabId(it) } + state.copy( + homeTabs = tabs, + selectedHomeTab = selectedTab, + ) } is TabListAction.SelectHomeTab -> state.copy(selectedHomeTab = action.id) } diff --git a/app-compose/src/main/kotlin/com/pitchedapps/frost/components/usecases/HomeTabsUseCases.kt b/app-compose/src/main/kotlin/com/pitchedapps/frost/web/usecases/HomeTabsUseCases.kt similarity index 96% rename from app-compose/src/main/kotlin/com/pitchedapps/frost/components/usecases/HomeTabsUseCases.kt rename to app-compose/src/main/kotlin/com/pitchedapps/frost/web/usecases/HomeTabsUseCases.kt index 327e59814..db5f93fda 100644 --- a/app-compose/src/main/kotlin/com/pitchedapps/frost/components/usecases/HomeTabsUseCases.kt +++ b/app-compose/src/main/kotlin/com/pitchedapps/frost/web/usecases/HomeTabsUseCases.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package com.pitchedapps.frost.components.usecases +package com.pitchedapps.frost.web.usecases import com.pitchedapps.frost.ext.WebTargetId import com.pitchedapps.frost.facebook.FbItem From 8c833bc97ff41d1cd82ee058b399f9e29c95b6df Mon Sep 17 00:00:00 2001 From: Allan Wang Date: Tue, 20 Jun 2023 23:52:15 -0700 Subject: [PATCH 6/6] Add back custom component. Original issue was because of inline value classes --- .../com/pitchedapps/frost/StartActivity.kt | 22 +---- .../frost/components/FrostComponents.kt | 1 + .../frost/compose/webview/FrostWebCompose.kt | 10 ++- .../com/pitchedapps/frost/ext/FrostExt.kt | 9 +- .../frost/main/MainScreenViewModel.kt | 1 - .../frost/web/usecases/HomeTabsUseCases.kt | 2 +- .../frost/web/usecases/TabUseCases.kt | 90 +++++++++++++++++++ .../{components => web/usecases}/UseCases.kt | 4 +- .../frost/webview/FrostChromeClients.kt | 6 +- .../com/pitchedapps/frost/webview/FrostWeb.kt | 34 ++----- .../frost/webview/FrostWebComposer.kt | 39 -------- .../frost/webview/FrostWebViewClients.kt | 8 +- 12 files changed, 131 insertions(+), 95 deletions(-) create mode 100644 app-compose/src/main/kotlin/com/pitchedapps/frost/web/usecases/TabUseCases.kt rename app-compose/src/main/kotlin/com/pitchedapps/frost/{components => web/usecases}/UseCases.kt (90%) delete mode 100644 app-compose/src/main/kotlin/com/pitchedapps/frost/webview/FrostWebComposer.kt diff --git a/app-compose/src/main/kotlin/com/pitchedapps/frost/StartActivity.kt b/app-compose/src/main/kotlin/com/pitchedapps/frost/StartActivity.kt index eb46ba46c..ebe13156c 100644 --- a/app-compose/src/main/kotlin/com/pitchedapps/frost/StartActivity.kt +++ b/app-compose/src/main/kotlin/com/pitchedapps/frost/StartActivity.kt @@ -29,9 +29,7 @@ import com.pitchedapps.frost.ext.launchActivity import com.pitchedapps.frost.facebook.FbItem import com.pitchedapps.frost.main.MainActivity import com.pitchedapps.frost.web.state.FrostWebStore -import com.pitchedapps.frost.web.state.TabAction -import com.pitchedapps.frost.web.state.TabListAction -import com.pitchedapps.frost.web.state.state.HomeTabSessionState +import com.pitchedapps.frost.web.usecases.UseCases import dagger.hilt.android.AndroidEntryPoint import javax.inject.Inject import kotlinx.coroutines.Dispatchers @@ -53,6 +51,7 @@ class StartActivity : ComponentActivity() { @Inject lateinit var dataStore: FrostDataStore @Inject lateinit var store: FrostWebStore + @Inject lateinit var useCases: UseCases override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -63,22 +62,7 @@ class StartActivity : ComponentActivity() { logger.atInfo().log("Starting Frost with id %s", id) // TODO load real tabs - store.dispatch(TabListAction.SetHomeTabs(data = listOf(FbItem.Feed, FbItem.Menu))) - // Test something scrollable - // store.dispatch( - // TabAction( - // tabId = HomeTabSessionState.homeTabId(0), - // TabAction.ContentAction.UpdateUrlAction( - // "https://github.com/AllanWang/Frost-for-Facebook" - // ), - // ), - // ) - store.dispatch( - TabAction( - tabId = HomeTabSessionState.homeTabId(1), - TabAction.ContentAction.UpdateUrlAction("https://github.com/AllanWang/KAU"), - ), - ) + useCases.homeTabs.setHomeTabs(listOf(FbItem.Feed, FbItem.Menu)) launchActivity( intentBuilder = { diff --git a/app-compose/src/main/kotlin/com/pitchedapps/frost/components/FrostComponents.kt b/app-compose/src/main/kotlin/com/pitchedapps/frost/components/FrostComponents.kt index 4563a39e9..d030e6c45 100644 --- a/app-compose/src/main/kotlin/com/pitchedapps/frost/components/FrostComponents.kt +++ b/app-compose/src/main/kotlin/com/pitchedapps/frost/components/FrostComponents.kt @@ -16,6 +16,7 @@ */ package com.pitchedapps.frost.components +import com.pitchedapps.frost.web.usecases.UseCases import javax.inject.Inject import javax.inject.Singleton diff --git a/app-compose/src/main/kotlin/com/pitchedapps/frost/compose/webview/FrostWebCompose.kt b/app-compose/src/main/kotlin/com/pitchedapps/frost/compose/webview/FrostWebCompose.kt index 958b18862..92fae8e93 100644 --- a/app-compose/src/main/kotlin/com/pitchedapps/frost/compose/webview/FrostWebCompose.kt +++ b/app-compose/src/main/kotlin/com/pitchedapps/frost/compose/webview/FrostWebCompose.kt @@ -39,7 +39,10 @@ import com.pitchedapps.frost.web.state.TabAction.ResponseAction.WebStepResponseA import com.pitchedapps.frost.web.state.get import com.pitchedapps.frost.web.state.state.ContentState import com.pitchedapps.frost.webview.FrostChromeClient +import com.pitchedapps.frost.webview.FrostWeb +import com.pitchedapps.frost.webview.FrostWebScoped import com.pitchedapps.frost.webview.FrostWebViewClient +import javax.inject.Inject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filter @@ -48,8 +51,11 @@ import kotlinx.coroutines.launch import mozilla.components.lib.state.ext.flow import mozilla.components.lib.state.ext.observeAsState -class FrostWebCompose( - val tabId: WebTargetId, +@FrostWebScoped +class FrostWebCompose +@Inject +internal constructor( + @FrostWeb val tabId: WebTargetId, private val store: FrostWebStore, private val client: FrostWebViewClient, private val chromeClient: FrostChromeClient, diff --git a/app-compose/src/main/kotlin/com/pitchedapps/frost/ext/FrostExt.kt b/app-compose/src/main/kotlin/com/pitchedapps/frost/ext/FrostExt.kt index f34d8f3fb..b4824192e 100644 --- a/app-compose/src/main/kotlin/com/pitchedapps/frost/ext/FrostExt.kt +++ b/app-compose/src/main/kotlin/com/pitchedapps/frost/ext/FrostExt.kt @@ -21,14 +21,19 @@ import com.pitchedapps.frost.proto.Account import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map +/* + * Cannot use inline value classes with Dagger due to Kapt: + * https://github.com/google/dagger/issues/2930 + */ + /** * Representation of unique frost account id. * * Account ids are identifiers specific to Frost, and group ids/info from other sites. */ -@JvmInline value class FrostAccountId(val id: Long) +data class FrostAccountId(val id: Long) -@JvmInline value class WebTargetId(val id: String) +data class WebTargetId(val id: String) /** * Representation of gecko context id. diff --git a/app-compose/src/main/kotlin/com/pitchedapps/frost/main/MainScreenViewModel.kt b/app-compose/src/main/kotlin/com/pitchedapps/frost/main/MainScreenViewModel.kt index 6a2ecb347..cb01f0bd4 100644 --- a/app-compose/src/main/kotlin/com/pitchedapps/frost/main/MainScreenViewModel.kt +++ b/app-compose/src/main/kotlin/com/pitchedapps/frost/main/MainScreenViewModel.kt @@ -41,7 +41,6 @@ internal constructor( val components: FrostComponents, val store: FrostWebStore, val frostWebComposer: FrostWebComposer, -// sample: FrostWebEntrySample, ) : ViewModel() { val contextIdFlow: Flow = diff --git a/app-compose/src/main/kotlin/com/pitchedapps/frost/web/usecases/HomeTabsUseCases.kt b/app-compose/src/main/kotlin/com/pitchedapps/frost/web/usecases/HomeTabsUseCases.kt index db5f93fda..51de39ae2 100644 --- a/app-compose/src/main/kotlin/com/pitchedapps/frost/web/usecases/HomeTabsUseCases.kt +++ b/app-compose/src/main/kotlin/com/pitchedapps/frost/web/usecases/HomeTabsUseCases.kt @@ -31,7 +31,7 @@ class HomeTabsUseCases @Inject internal constructor(private val store: FrostWebS * * If there are existing tabs, they will be replaced. */ - fun createHomeTabs(items: List) { + fun setHomeTabs(items: List) { store.dispatch(SetHomeTabs(items)) } diff --git a/app-compose/src/main/kotlin/com/pitchedapps/frost/web/usecases/TabUseCases.kt b/app-compose/src/main/kotlin/com/pitchedapps/frost/web/usecases/TabUseCases.kt new file mode 100644 index 000000000..37a68ef6e --- /dev/null +++ b/app-compose/src/main/kotlin/com/pitchedapps/frost/web/usecases/TabUseCases.kt @@ -0,0 +1,90 @@ +/* + * Copyright 2023 Allan Wang + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.pitchedapps.frost.web.usecases + +import com.pitchedapps.frost.ext.WebTargetId +import com.pitchedapps.frost.web.state.FrostWebStore +import com.pitchedapps.frost.web.state.TabAction +import javax.inject.Inject + +class TabUseCases +@Inject +internal constructor( + private val store: FrostWebStore, + val requests: TabRequestUseCases, + val responses: TabResponseUseCases, +) { + fun updateUrl(tabId: WebTargetId, url: String) { + store.dispatch(TabAction(tabId = tabId, action = TabAction.ContentAction.UpdateUrlAction(url))) + } + + fun updateTitle(tabId: WebTargetId, title: String?) { + store.dispatch( + TabAction( + tabId = tabId, + action = TabAction.ContentAction.UpdateTitleAction(title), + ), + ) + } + + fun updateNavigation(tabId: WebTargetId, canGoBack: Boolean, canGoForward: Boolean) { + store.dispatch( + TabAction( + tabId = tabId, + action = + TabAction.ContentAction.UpdateNavigationAction( + canGoBack = canGoBack, + canGoForward = canGoForward, + ), + ), + ) + } +} + +class TabRequestUseCases @Inject internal constructor(private val store: FrostWebStore) { + fun requestUrl(tabId: WebTargetId, url: String) { + store.dispatch(TabAction(tabId = tabId, action = TabAction.UserAction.LoadUrlAction(url))) + } + + fun goBack(tabId: WebTargetId) { + store.dispatch(TabAction(tabId = tabId, action = TabAction.UserAction.GoBackAction)) + } + + fun goForward(tabId: WebTargetId) { + store.dispatch(TabAction(tabId = tabId, action = TabAction.UserAction.GoForwardAction)) + } +} + +class TabResponseUseCases @Inject internal constructor(private val store: FrostWebStore) { + fun respondUrl(tabId: WebTargetId, url: String) { + store.dispatch( + TabAction( + tabId = tabId, + action = TabAction.ResponseAction.LoadUrlResponseAction(url), + ), + ) + } + + fun respondSteps(tabId: WebTargetId, steps: Int) { + store.dispatch( + TabAction( + tabId = tabId, + action = TabAction.ResponseAction.WebStepResponseAction(steps), + ), + ) + } +} diff --git a/app-compose/src/main/kotlin/com/pitchedapps/frost/components/UseCases.kt b/app-compose/src/main/kotlin/com/pitchedapps/frost/web/usecases/UseCases.kt similarity index 90% rename from app-compose/src/main/kotlin/com/pitchedapps/frost/components/UseCases.kt rename to app-compose/src/main/kotlin/com/pitchedapps/frost/web/usecases/UseCases.kt index 6342c0c1b..209348f0c 100644 --- a/app-compose/src/main/kotlin/com/pitchedapps/frost/components/UseCases.kt +++ b/app-compose/src/main/kotlin/com/pitchedapps/frost/web/usecases/UseCases.kt @@ -14,9 +14,8 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package com.pitchedapps.frost.components +package com.pitchedapps.frost.web.usecases -import com.pitchedapps.frost.web.usecases.HomeTabsUseCases import javax.inject.Inject import javax.inject.Singleton @@ -30,4 +29,5 @@ class UseCases @Inject internal constructor( val homeTabs: HomeTabsUseCases, + val tabs: TabUseCases, ) diff --git a/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/FrostChromeClients.kt b/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/FrostChromeClients.kt index bc819f3ff..4b562a832 100644 --- a/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/FrostChromeClients.kt +++ b/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/FrostChromeClients.kt @@ -27,9 +27,13 @@ import com.pitchedapps.frost.web.state.TabAction import com.pitchedapps.frost.web.state.TabAction.ContentAction.UpdateProgressAction import com.pitchedapps.frost.web.state.TabAction.ContentAction.UpdateTitleAction import com.pitchedapps.frost.web.state.get +import javax.inject.Inject /** The default chrome client */ -class FrostChromeClient(private val tabId: WebTargetId, private val store: FrostWebStore) : +@FrostWebScoped +class FrostChromeClient +@Inject +internal constructor(@FrostWeb private val tabId: WebTargetId, private val store: FrostWebStore) : WebChromeClient() { private fun FrostWebStore.dispatch(action: TabAction.Action) { diff --git a/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/FrostWeb.kt b/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/FrostWeb.kt index dc1232108..b82c01b2b 100644 --- a/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/FrostWeb.kt +++ b/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/FrostWeb.kt @@ -16,19 +16,14 @@ */ package com.pitchedapps.frost.webview -import android.webkit.WebViewClient +import com.pitchedapps.frost.compose.webview.FrostWebCompose import com.pitchedapps.frost.ext.WebTargetId -import com.pitchedapps.frost.web.FrostWebHelper -import com.pitchedapps.frost.web.state.FrostWebStore -import com.pitchedapps.frost.webview.injection.FrostJsInjectors import dagger.BindsInstance -import dagger.Module -import dagger.Provides import dagger.hilt.DefineComponent import dagger.hilt.EntryPoint import dagger.hilt.EntryPoints import dagger.hilt.InstallIn -import dagger.hilt.components.SingletonComponent +import dagger.hilt.android.components.ViewModelComponent import javax.inject.Inject import javax.inject.Qualifier import javax.inject.Scope @@ -45,45 +40,32 @@ annotation class FrostWebScoped @Qualifier annotation class FrostWeb -@FrostWebScoped @DefineComponent(parent = SingletonComponent::class) interface FrostWebComponent +@FrostWebScoped @DefineComponent(parent = ViewModelComponent::class) interface FrostWebComponent @DefineComponent.Builder interface FrostWebComponentBuilder { - @BindsInstance fun tabId(@FrostWeb tabId: WebTargetId): FrostWebComponentBuilder + fun tabId(@BindsInstance @FrostWeb tabId: WebTargetId): FrostWebComponentBuilder fun build(): FrostWebComponent } -@Module -@InstallIn(FrostWebComponent::class) -internal object FrostWebModule { - - @Provides - fun client( - @FrostWeb tabId: WebTargetId, - store: FrostWebStore, - webHelper: FrostWebHelper, - frostJsInjectors: FrostJsInjectors, - ): WebViewClient = FrostWebViewClient(tabId, store, webHelper, frostJsInjectors) -} - /** * Using this injection seems to be buggy, leading to an invalid param tabId error: * * Cause: not a valid name: tabId-4xHwVBUParam */ -class FrostWebEntrySample +class FrostWebComposer @Inject internal constructor(private val frostWebComponentBuilder: FrostWebComponentBuilder) { - fun test(tabId: WebTargetId): WebViewClient { + fun create(tabId: WebTargetId): FrostWebCompose { val component = frostWebComponentBuilder.tabId(tabId).build() - return EntryPoints.get(component, FrostWebEntryPoint::class.java).client() + return EntryPoints.get(component, FrostWebEntryPoint::class.java).compose() } @EntryPoint @InstallIn(FrostWebComponent::class) interface FrostWebEntryPoint { - fun client(): WebViewClient + fun compose(): FrostWebCompose } } diff --git a/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/FrostWebComposer.kt b/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/FrostWebComposer.kt deleted file mode 100644 index 9ca3860df..000000000 --- a/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/FrostWebComposer.kt +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2023 Allan Wang - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package com.pitchedapps.frost.webview - -import com.pitchedapps.frost.compose.webview.FrostWebCompose -import com.pitchedapps.frost.ext.WebTargetId -import com.pitchedapps.frost.web.FrostWebHelper -import com.pitchedapps.frost.web.state.FrostWebStore -import com.pitchedapps.frost.webview.injection.FrostJsInjectors -import javax.inject.Inject - -class FrostWebComposer -@Inject -internal constructor( - private val store: FrostWebStore, - private val webHelper: FrostWebHelper, - private val frostJsInjectors: FrostJsInjectors, -) { - - fun create(tabId: WebTargetId): FrostWebCompose { - val client = FrostWebViewClient(tabId, store, webHelper, frostJsInjectors) - val chromeClient = FrostChromeClient(tabId, store) - return FrostWebCompose(tabId, store, client, chromeClient) - } -} diff --git a/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/FrostWebViewClients.kt b/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/FrostWebViewClients.kt index 0b89c7556..4ba6219ae 100644 --- a/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/FrostWebViewClients.kt +++ b/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/FrostWebViewClients.kt @@ -36,6 +36,7 @@ import com.pitchedapps.frost.web.state.TabAction.ContentAction.UpdateProgressAct import com.pitchedapps.frost.web.state.TabAction.ContentAction.UpdateTitleAction import com.pitchedapps.frost.webview.injection.FrostJsInjectors import java.io.ByteArrayInputStream +import javax.inject.Inject /** * Created by Allan Wang on 2017-05-31. @@ -63,8 +64,11 @@ abstract class BaseWebViewClient : WebViewClient() { } /** The default webview client */ -class FrostWebViewClient( - private val tabId: WebTargetId, +@FrostWebScoped +class FrostWebViewClient +@Inject +internal constructor( + @FrostWeb private val tabId: WebTargetId, private val store: FrostWebStore, override val webHelper: FrostWebHelper, private val frostJsInjectors: FrostJsInjectors,