Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
import java.io.OutputStream;

public class WordPressDB {
private static final int DATABASE_VERSION = 70;
private static final int DATABASE_VERSION = 71;


// Warning renaming DATABASE_NAME could break previous App backups (see: xml/backup_scheme.xml)
Expand Down Expand Up @@ -187,6 +187,9 @@ public WordPressDB(Context ctx) {
case 69:
// add editor theme styles site setting
mDb.execSQL(SiteSettingsModel.ADD_USE_THEME_STYLES);
case 70:
// add third-party blocks site setting
mDb.execSQL(SiteSettingsModel.ADD_USE_THIRD_PARTY_BLOCKS);
}
mDb.setVersion(DATABASE_VERSION);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package org.wordpress.android.datasets

import org.wordpress.android.fluxc.model.SiteModel
import org.wordpress.android.models.SiteSettingsModel

/**
* Provides site-level settings for a given [SiteModel].
*/
interface SiteSettingsProvider {
fun getSettings(site: SiteModel): SiteSettingsModel?
fun isBlockEditorDefault(site: SiteModel): Boolean
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package org.wordpress.android.datasets

import org.wordpress.android.fluxc.model.SiteModel
import org.wordpress.android.models.SiteSettingsModel
import javax.inject.Inject
import javax.inject.Singleton

@Singleton
class SiteSettingsProviderImpl @Inject constructor() :
SiteSettingsProvider {
override fun getSettings(site: SiteModel): SiteSettingsModel? {
val cursor = SiteSettingsTable.getSettings(site.id.toLong())
?: return null
return cursor.use {
if (it.moveToFirst()) {
SiteSettingsModel().also { model ->
model.deserializeOptionsDatabaseCursor(it, null)
}
} else {
null
}
}
}

override fun isBlockEditorDefault(site: SiteModel): Boolean {
val editor = site.mobileEditor
if (editor.isNullOrEmpty()) return true
return site.isWPComSimpleSite || editor == GUTENBERG_EDITOR_NAME
}

private companion object {
const val GUTENBERG_EDITOR_NAME = "gutenberg"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ public class SiteSettingsModel {
private static final String JETPACK_SEARCH_SUPPORTED_COLUMN_NAME = "jetpackSearchSupported";
private static final String JETPACK_SEARCH_ENABLED_COLUMN_NAME = "jetpackSearchEnabled";
private static final String USE_THEME_STYLES_COLUMN_NAME = "useThemeStyles";
private static final String USE_THIRD_PARTY_BLOCKS_COLUMN_NAME = "useThirdPartyBlocks";

public static final String SETTINGS_TABLE_NAME = "site_settings";

Expand Down Expand Up @@ -107,6 +108,9 @@ public class SiteSettingsModel {
+ " add " + SITE_ICON_COLUMN_NAME + " INTEGER;";
public static final String ADD_USE_THEME_STYLES = "alter table " + SETTINGS_TABLE_NAME
+ " add " + USE_THEME_STYLES_COLUMN_NAME + " BOOLEAN DEFAULT 1;";
public static final String ADD_USE_THIRD_PARTY_BLOCKS = "alter table " + SETTINGS_TABLE_NAME
+ " add " + USE_THIRD_PARTY_BLOCKS_COLUMN_NAME
+ " BOOLEAN DEFAULT 0;";

public static final String CREATE_SETTINGS_TABLE_SQL =
"CREATE TABLE IF NOT EXISTS "
Expand Down Expand Up @@ -198,6 +202,7 @@ public class SiteSettingsModel {
public boolean jetpackSearchSupported;
public boolean jetpackSearchEnabled;
public boolean useThemeStyles = true;
public boolean useThirdPartyBlocks;
public String quotaDiskSpace;

@Override
Expand Down Expand Up @@ -243,6 +248,7 @@ && equals(timezone, otherModel.timezone)
&& jetpackSearchEnabled == otherModel.jetpackSearchEnabled
&& jetpackSearchSupported == otherModel.jetpackSearchSupported
&& useThemeStyles == otherModel.useThemeStyles
&& useThirdPartyBlocks == otherModel.useThirdPartyBlocks
&& maxLinks == otherModel.maxLinks
&& equals(defaultPostFormat, otherModel.defaultPostFormat)
&& holdForModeration != null
Expand Down Expand Up @@ -309,6 +315,7 @@ public void copyFrom(SiteSettingsModel other) {
jetpackSearchSupported = other.jetpackSearchSupported;
jetpackSearchEnabled = other.jetpackSearchEnabled;
useThemeStyles = other.useThemeStyles;
useThirdPartyBlocks = other.useThirdPartyBlocks;
if (other.holdForModeration != null) {
holdForModeration = new ArrayList<>(other.holdForModeration);
}
Expand Down Expand Up @@ -374,6 +381,7 @@ public void deserializeOptionsDatabaseCursor(Cursor cursor, SparseArrayCompat<Ca
jetpackSearchSupported = getBooleanFromCursor(cursor, JETPACK_SEARCH_SUPPORTED_COLUMN_NAME);
jetpackSearchEnabled = getBooleanFromCursor(cursor, JETPACK_SEARCH_ENABLED_COLUMN_NAME);
useThemeStyles = getBooleanFromCursor(cursor, USE_THEME_STYLES_COLUMN_NAME);
useThirdPartyBlocks = getBooleanFromCursor(cursor, USE_THIRD_PARTY_BLOCKS_COLUMN_NAME);

String moderationKeys = getStringFromCursor(cursor, MODERATION_KEYS_COLUMN_NAME);
String denylistKeys = getStringFromCursor(cursor, DENYLIST_KEYS_COLUMN_NAME);
Expand Down Expand Up @@ -467,6 +475,7 @@ public ContentValues serializeToDatabase() {
values.put(JETPACK_SEARCH_SUPPORTED_COLUMN_NAME, jetpackSearchSupported);
values.put(JETPACK_SEARCH_ENABLED_COLUMN_NAME, jetpackSearchEnabled);
values.put(USE_THEME_STYLES_COLUMN_NAME, useThemeStyles);
values.put(USE_THIRD_PARTY_BLOCKS_COLUMN_NAME, useThirdPartyBlocks);

StringBuilder moderationKeys = new StringBuilder();
if (holdForModeration != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import org.wordpress.android.datasets.SiteSettingsProvider
import org.wordpress.android.datasets.SiteSettingsProviderImpl
import org.wordpress.android.ui.posts.IPostFreshnessChecker
import org.wordpress.android.ui.posts.PostFreshnessCheckerImpl
import javax.inject.Singleton
Expand All @@ -13,5 +15,12 @@ import javax.inject.Singleton
class PostModule {
@Singleton
@Provides
fun providePostFreshnessChecker(): IPostFreshnessChecker = PostFreshnessCheckerImpl()
fun providePostFreshnessChecker(): IPostFreshnessChecker =
PostFreshnessCheckerImpl()

@Singleton
@Provides
fun provideSiteSettingsProvider(
impl: SiteSettingsProviderImpl
): SiteSettingsProvider = impl
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
package org.wordpress.android.repositories

import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.withContext
import org.wordpress.android.fluxc.model.SiteModel
import org.wordpress.android.fluxc.network.rest.wpapi.rs.WpApiClientProvider
import org.wordpress.android.ui.prefs.AppPrefsWrapper
import org.wordpress.android.fluxc.persistence.EditorSettingsSqlUtils
import org.wordpress.android.modules.IO_THREAD
import org.wordpress.android.util.AppLog
import org.wordpress.android.util.AppLog.T
import rs.wordpress.api.kotlin.WpRequestResult
import javax.inject.Inject
import javax.inject.Named
import javax.inject.Singleton

@Singleton
class EditorSettingsRepository @Inject constructor(
private val wpApiClientProvider: WpApiClientProvider,
private val appPrefsWrapper: AppPrefsWrapper,
private val themeRepository: ThemeRepository,
@Named(IO_THREAD) private val ioDispatcher: CoroutineDispatcher
) {
private val editorSettingsSqlUtils = EditorSettingsSqlUtils()

fun hasCachedCapabilities(site: SiteModel): Boolean =
appPrefsWrapper.hasSiteEditorCapabilities(site)

/**
* Returns whether the site is known to support the
* `wp-block-editor/v1/settings` endpoint, based on
* cached editor settings or a previously persisted
* result from [fetchEditorCapabilitiesForSite].
*/
fun getSupportsEditorSettingsForSite(site: SiteModel): Boolean {
val hasExisting =
editorSettingsSqlUtils.getEditorSettingsForSite(site) != null
val cachedPref =
appPrefsWrapper.getSiteSupportsEditorSettings(site)
return hasExisting || cachedPref
}

/**
* Returns whether the site is known to support the
* `wpcom/v2/editor-assets` endpoint, based on a
* previously persisted result from
* [fetchEditorCapabilitiesForSite].
*/
fun getSupportsEditorAssetsForSite(
site: SiteModel
): Boolean =
appPrefsWrapper.getSiteSupportsEditorAssets(site)

/**
* Returns whether the site's active theme is a block
* theme, based on a previously persisted result from
* [fetchEditorCapabilitiesForSite].
*/
fun getThemeSupportsBlockStyles(
site: SiteModel
): Boolean =
appPrefsWrapper.getSiteThemeIsBlockTheme(site)

/**
* Queries the site's REST API to check whether the
* `wp-block-editor/v1/settings` and
* `wpcom/v2/editor-assets` routes are available,
* and fetches the current theme to determine if it
* is a block theme. All results are persisted so
* that [getSupportsEditorSettingsForSite],
* [getSupportsEditorAssetsForSite], and
* [getThemeSupportsBlockStyles] return them
* synchronously on future calls.
*
* @return `true` when both checks complete without
* transport-level failures.
*/
suspend fun fetchEditorCapabilitiesForSite(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this can be simplified to:

suspend fun fetchEditorCapabilitiesForSite(
        site: SiteModel
    ): Boolean = withContext(ioDispatcher) {
        val results = awaitAll(
            async { fetchRouteSupport(site) },
            async { fetchThemeBlockStyleSupport(site) }
        )
        results.all { it }
    }

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice – done in 334c823

site: SiteModel
): Boolean = withContext(ioDispatcher) {
val results = awaitAll(
async { fetchRouteSupport(site) },
async { fetchThemeBlockStyleSupport(site) }
)
results.all { it }
}

@Suppress("TooGenericExceptionCaught")
private suspend fun fetchRouteSupport(
site: SiteModel
): Boolean = try {
val client =
wpApiClientProvider.getWpApiClient(site)
val resolver =
wpApiClientProvider.getApiUrlResolver(site)
val response =
client.request { it.apiRoot().get() }

if (response is WpRequestResult.Success) {
val data = response.response.data
appPrefsWrapper
.setSiteSupportsEditorSettings(
site,
data.hasRouteForEndpoint(
resolver,
"/wp-block-editor/v1",
"settings"
)
)
appPrefsWrapper
.setSiteSupportsEditorAssets(
site,
data.hasRouteForEndpoint(
resolver,
"/wp-block-editor/v1",
"assets"
)
)
true
} else {
false
}
} catch (e: CancellationException) {
throw e
} catch (e: Exception) {
AppLog.e(
T.EDITOR,
"Failed to fetch route support" +
" for site=${site.name}",
e
)
false
}

@Suppress("TooGenericExceptionCaught")
private suspend fun fetchThemeBlockStyleSupport(
site: SiteModel
): Boolean = try {
val theme =
themeRepository.fetchCurrentTheme(site)
if (theme != null) {
AppLog.d(
T.EDITOR,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is nitpicky, but why is this split into five string fragments?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it was originally four (the main one, plus a line per variable), then I added the non-variable line. 4bf1903 restores my original intent.

"EditorSettingsRepository: theme fetched for " +
"site=${site.name}, " +
"themeName=${theme.name}, " +
"isBlockTheme=${theme.isBlockTheme}"
)
appPrefsWrapper.setSiteThemeIsBlockTheme(
site, theme.isBlockTheme
)
true
} else {
false
}
} catch (e: CancellationException) {
throw e
} catch (e: Exception) {
AppLog.e(
T.EDITOR,
"Failed to fetch theme info" +
" for site=${site.name}",
e
)
false
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package org.wordpress.android.repositories

import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.withContext
import org.wordpress.android.fluxc.model.SiteModel
import org.wordpress.android.fluxc.network.rest.wpapi.rs.WpApiClientProvider
import org.wordpress.android.modules.IO_THREAD
import rs.wordpress.api.kotlin.WpRequestResult
import uniffi.wp_api.ThemeListParams
import uniffi.wp_api.ThemeStatus
import uniffi.wp_api.ThemeWithEditContext
import javax.inject.Inject
import javax.inject.Named
import javax.inject.Singleton

@Singleton
class ThemeRepository @Inject constructor(
private val wpApiClientProvider: WpApiClientProvider,
@Named(IO_THREAD) private val ioDispatcher: CoroutineDispatcher
) {
/**
* Fetches the current active theme for the given site
* via the `wp/v2/themes?status=active` endpoint.
*/
suspend fun fetchCurrentTheme(site: SiteModel): ThemeWithEditContext? =
withContext(ioDispatcher) {
val client = wpApiClientProvider.getWpApiClient(site)
val response = client.request {
it.themes().listWithEditContext(ThemeListParams(
status = ThemeStatus.Active
))
}

when (response) {
is WpRequestResult.Success ->
response.response.data.firstOrNull()
else -> null
}
}
}
Loading
Loading