Skip to content

Commit

Permalink
GIF Viewer (#3605)
Browse files Browse the repository at this point in the history
* ios player

autoplay after recycle

remove all items from AVPlayer queue

recurururururursion

use managers in the view

add prefetch

make sure player items stay in order

add controller and item managers

start of the view

create module, ios

* android player

smoother

basic caching

prep cache

somewhat works

backup

other files

android impl

blegh

lets go

touchup

add prefetch to js

use caching

* bogus testing commit

* add dims to type

* save

* add the dimensions to the embed info

* add a new case

* add a new case

* limit this case to giphy

* use gate

* Revert "bogus testing commit"

This reverts commit b3c8751.

* add web player base

* flip mp4/webp

* basic mp4 player for web

* move some stuff into `ExternalLinkEmbed` instead

* use a class component for web

* remove extra component

* add `onPlayerStateChange` event type on web

* layer properly

* fix tests

* add new test

* about ready. native portions done, a few touch ups on web needed

show placeholder on ios

fix type

rm log

display thumbnail until video is ready to play

add oncanplay, playsinline

remove unused method

add `isLoaded` change event

release player when finished

apply gc to the view

cleanup logs

android gc

rm log

automatic gc for assets

make `nativeRef` private

remove unnecessary `await`

cleanup

rev log

only play on prepare whenever needed

rm unused

perfperfperf

rm var

comment + android width

native height calculations

rm pressable

add event dispatcher on android

add event dispatcher on ios

* ready to test ios

fix autoplay ios

clean

oops

* autoplay on web

* normalize across all platforms

add check for `ALT:`

separate gif embed logic to another file

handle permissions requests

flatten web styles

normalize styles

normalize styles

prefetch functions

pause animatable on foreground android

nits

one more oops

idk where that code went

lint

rethink the usage

wrap up

android

clear bg

update gradle

more android

rename dir

update android namespace

web

ios

add deps

use webp

rm unused

update types

use webp on mobile

* rm gate from types

* remove unused event param

* only start placeholder op if doesn't exist in disk cache

* fix gifs animating on app resume android

* remove comment

* add `isLoaded` for ios

* add `isLoaded` to Android

* onload for web

* add visual loading state

* rm a log

* implement isloaded for android

* dialogs

* replace `webpSource` with `source`

* update prop name

* Move to Tenor for GIFs (#3654)

* update some urls

* right order for dimensions

* add GIF coder for ios

* remove giphy check

* rewrite tenor urls

* remove all the unnecessary stuff for consent

* rm print

* rm log

* check if id and filename are strings

* full size playback controls

* pass tests

* add accessibility to gifs

* use `onPlay` and `onPause`

* rm unused logic for description

* add accessibility label to the controls

* add gif into to external embed in composer

* make it optional

* gif dimensions

* make the jsx look nicer

---------

Co-authored-by: Dan Abramov <[email protected]>
Co-authored-by: Samuel Newman <[email protected]>
  • Loading branch information
3 people authored Apr 23, 2024
1 parent fe9b3f0 commit cbb817b
Show file tree
Hide file tree
Showing 22 changed files with 1,129 additions and 171 deletions.
42 changes: 20 additions & 22 deletions __tests__/lib/string.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,12 @@ describe('parseEmbedPlayerFromUrl', () => {
'https://tenor.com/view',
'https://tenor.com/view/gifId.gif',
'https://tenor.com/intl/view/gifId.gif',

'https://media.tenor.com/someID_AAAAC/someName.gif?hh=100&ww=100',
'https://media.tenor.com/someID_AAAAC/someName.gif',
'https://media.tenor.com/someID/someName.gif',
'https://media.tenor.com/someID',
'https://media.tenor.com',
]

const outputs = [
Expand Down Expand Up @@ -628,20 +634,14 @@ describe('parseEmbedPlayerFromUrl', () => {
},
undefined,
undefined,

{
type: 'giphy_gif',
source: 'giphy',
isGif: true,
hideDetails: true,
metaUri: 'https://giphy.com/gifs/39248209509382934029',
playerUri: 'https://i.giphy.com/media/39248209509382934029/200.mp4',
dimensions: {
width: 100,
height: 100,
},
playerUri: 'https://i.giphy.com/media/39248209509382934029/200.webp',
},

{
type: 'giphy_gif',
source: 'giphy',
Expand Down Expand Up @@ -736,29 +736,27 @@ describe('parseEmbedPlayerFromUrl', () => {
playerUri: 'https://i.giphy.com/media/gifId/200.webp',
},

{
type: 'tenor_gif',
source: 'tenor',
isGif: true,
hideDetails: true,
playerUri: 'https://tenor.com/view/gifId.gif',
},
undefined,
undefined,
undefined,
undefined,
undefined,

{
type: 'tenor_gif',
source: 'tenor',
isGif: true,
hideDetails: true,
playerUri: 'https://tenor.com/view/gifId.gif',
},
{
type: 'tenor_gif',
source: 'tenor',
isGif: true,
hideDetails: true,
playerUri: 'https://tenor.com/intl/view/gifId.gif',
playerUri: 'https://t.gifs.bsky.app/someID_AAAAM/someName.gif',
dimensions: {
width: 100,
height: 100,
},
},
undefined,
undefined,
undefined,
undefined,
]

it('correctly grabs the correct id from uri', () => {
Expand Down
98 changes: 98 additions & 0 deletions modules/expo-bluesky-gif-view/android/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'maven-publish'

group = 'expo.modules.blueskygifview'
version = '0.5.0'

buildscript {
def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
if (expoModulesCorePlugin.exists()) {
apply from: expoModulesCorePlugin
applyKotlinExpoModulesCorePlugin()
}

// Simple helper that allows the root project to override versions declared by this library.
ext.safeExtGet = { prop, fallback ->
rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
}

// Ensures backward compatibility
ext.getKotlinVersion = {
if (ext.has("kotlinVersion")) {
ext.kotlinVersion()
} else {
ext.safeExtGet("kotlinVersion", "1.8.10")
}
}

repositories {
mavenCentral()
}

dependencies {
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${getKotlinVersion()}")
}
}

afterEvaluate {
publishing {
publications {
release(MavenPublication) {
from components.release
}
}
repositories {
maven {
url = mavenLocal().url
}
}
}
}

android {
compileSdkVersion safeExtGet("compileSdkVersion", 33)

def agpVersion = com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION
if (agpVersion.tokenize('.')[0].toInteger() < 8) {
compileOptions {
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
}

kotlinOptions {
jvmTarget = JavaVersion.VERSION_11.majorVersion
}
}

namespace "expo.modules.blueskygifview"
defaultConfig {
minSdkVersion safeExtGet("minSdkVersion", 21)
targetSdkVersion safeExtGet("targetSdkVersion", 34)
versionCode 1
versionName "0.5.0"
}
lintOptions {
abortOnError false
}
publishing {
singleVariant("release") {
withSourcesJar()
}
}
}

repositories {
mavenCentral()
}

dependencies {
implementation 'androidx.appcompat:appcompat:1.6.1'
def GLIDE_VERSION = "4.13.2"

implementation project(':expo-modules-core')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${getKotlinVersion()}"

// Keep glide version up to date with expo-image so that we don't have duplicate deps
implementation 'com.github.bumptech.glide:glide:4.13.2'
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<manifest>
</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package expo.modules.blueskygifview

import android.content.Context
import android.graphics.Canvas
import android.graphics.drawable.Animatable
import androidx.appcompat.widget.AppCompatImageView

class AppCompatImageViewExtended(context: Context, private val parent: GifView): AppCompatImageView(context) {
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)

if (this.drawable is Animatable) {
if (!parent.isLoaded) {
parent.isLoaded = true
parent.firePlayerStateChange()
}

if (!parent.isPlaying) {
this.pause()
}
}
}

fun pause() {
val drawable = this.drawable
if (drawable is Animatable) {
drawable.stop()
}
}

fun play() {
val drawable = this.drawable
if (drawable is Animatable) {
drawable.start()
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package expo.modules.blueskygifview

import com.bumptech.glide.Glide
import com.bumptech.glide.load.engine.DiskCacheStrategy
import expo.modules.kotlin.modules.Module
import expo.modules.kotlin.modules.ModuleDefinition

class ExpoBlueskyGifViewModule : Module() {
override fun definition() = ModuleDefinition {
Name("ExpoBlueskyGifView")

AsyncFunction("prefetchAsync") { sources: List<String> ->
val activity = appContext.currentActivity ?: return@AsyncFunction
val glide = Glide.with(activity)

sources.forEach { source ->
glide
.download(source)
.diskCacheStrategy(DiskCacheStrategy.DATA)
.submit()
}
}

View(GifView::class) {
Events(
"onPlayerStateChange"
)

Prop("source") { view: GifView, source: String ->
view.source = source
}

Prop("placeholderSource") { view: GifView, source: String ->
view.placeholderSource = source
}

Prop("autoplay") { view: GifView, autoplay: Boolean ->
view.autoplay = autoplay
}

AsyncFunction("playAsync") { view: GifView ->
view.play()
}

AsyncFunction("pauseAsync") { view: GifView ->
view.pause()
}

AsyncFunction("toggleAsync") { view: GifView ->
view.toggle()
}
}
}
}
Loading

0 comments on commit cbb817b

Please sign in to comment.