Skip to content

Commit 8755954

Browse files
committed
Fix tab not being inserted when opening in new tab
1 parent 53cbab4 commit 8755954

File tree

5 files changed

+122
-15
lines changed

5 files changed

+122
-15
lines changed

app/src/main/java/com/duckduckgo/app/tabs/TabFeatureFlags.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,7 @@ interface TabManagerFeatureFlags {
3232

3333
@Toggle.DefaultValue(DefaultFeatureValue.FALSE)
3434
fun multiSelection(): Toggle
35+
36+
@Toggle.DefaultValue(DefaultFeatureValue.TRUE)
37+
fun tabInsertionFixes(): Toggle
3538
}

app/src/main/java/com/duckduckgo/app/tabs/db/TabsDao.kt

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import dagger.SingleInstanceIn
2626
import java.time.LocalDateTime
2727
import kotlin.math.max
2828
import kotlinx.coroutines.flow.Flow
29+
import logcat.logcat
2930

3031
@Dao
3132
@SingleInstanceIn(AppScope::class)
@@ -160,10 +161,24 @@ abstract class TabsDao {
160161
abstract fun incrementPositionStartingAt(position: Int)
161162

162163
@Transaction
163-
open fun addAndSelectTab(tab: TabEntity) {
164-
deleteBlankTabs()
165-
insertTab(tab)
166-
insertTabSelection(TabSelectionEntity(tabId = tab.tabId))
164+
open fun addAndSelectTab(tab: TabEntity, updateIfBlankParent: Boolean = false) {
165+
try {
166+
val parent = tab.sourceTabId?.let { tab(it) }
167+
val newTab = if (updateIfBlankParent && parent != null && parent.url == null) {
168+
/*
169+
* If the parent tab is blank, we need to update the parent tab with the previous
170+
* one. Otherwise, foreign key constrains won't be met
171+
*/
172+
tab.copy(sourceTabId = parent.sourceTabId, position = parent.position)
173+
} else {
174+
tab
175+
}
176+
deleteBlankTabs()
177+
insertTab(newTab)
178+
insertTabSelection(TabSelectionEntity(tabId = newTab.tabId))
179+
} catch (e: Exception) {
180+
logcat { "Error adding and selecting tab: $e" }
181+
}
167182
}
168183

169184
@Transaction

app/src/main/java/com/duckduckgo/app/tabs/model/TabDataRepository.kt

Lines changed: 45 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -93,12 +93,21 @@ class TabDataRepository @Inject constructor(
9393

9494
private var purgeDeletableTabsJob = ConflatedJob()
9595

96+
private var tabInsertionFixesFlag: Boolean? = null
97+
9698
override suspend fun add(
9799
url: String?,
98100
skipHome: Boolean,
99101
): String = withContext(dispatchers.io()) {
100102
val tabId = generateTabId()
101-
add(tabId, buildSiteData(url, tabId), skipHome = skipHome, isDefaultTab = false)
103+
val flag = getAndCacheTabInsertionFixesFlag()
104+
val siteData = if (flag) {
105+
buildSiteData(url, tabId)
106+
} else {
107+
buildSiteDataSync(url, tabId)
108+
}
109+
add(tabId, siteData, skipHome = skipHome, isDefaultTab = false, updateIfBlankParent = flag)
110+
102111
return@withContext tabId
103112
}
104113

@@ -108,34 +117,52 @@ class TabDataRepository @Inject constructor(
108117
sourceTabId: String,
109118
): String = withContext(dispatchers.io()) {
110119
val tabId = generateTabId()
111-
120+
val flag = getAndCacheTabInsertionFixesFlag()
121+
val siteData = if (flag) {
122+
buildSiteData(url, tabId)
123+
} else {
124+
buildSiteDataSync(url, tabId)
125+
}
112126
add(
113127
tabId = tabId,
114-
data = buildSiteData(url, tabId),
128+
data = siteData,
115129
skipHome = skipHome,
116130
isDefaultTab = false,
117131
sourceTabId = sourceTabId,
132+
updateIfBlankParent = flag,
118133
)
119134

120135
return@withContext tabId
121136
}
122137

123138
override suspend fun addDefaultTab(): String = withContext(dispatchers.io()) {
124139
val tabId = generateTabId()
125-
140+
val flag = getAndCacheTabInsertionFixesFlag()
141+
val siteData = if (flag) {
142+
buildSiteData(url = null, tabId = tabId)
143+
} else {
144+
buildSiteDataSync(url = null, tabId)
145+
}
126146
add(
127147
tabId = tabId,
128-
data = buildSiteData(url = null, tabId = tabId),
148+
data = siteData,
129149
skipHome = false,
130150
isDefaultTab = true,
151+
updateIfBlankParent = flag,
131152
)
132153

133154
return@withContext tabId
134155
}
135156

157+
private suspend fun getAndCacheTabInsertionFixesFlag(): Boolean {
158+
return tabInsertionFixesFlag ?: withContext(dispatchers.io()) {
159+
tabManagerFeatureFlags.tabInsertionFixes().isEnabled()
160+
}.also { tabInsertionFixesFlag = it }
161+
}
162+
136163
private fun generateTabId() = UUID.randomUUID().toString()
137164

138-
private fun buildSiteData(url: String?, tabId: String): MutableLiveData<Site> {
165+
private fun buildSiteDataSync(url: String?, tabId: String): MutableLiveData<Site> {
139166
val data = MutableLiveData<Site>()
140167
url?.let {
141168
val siteMonitor = siteFactory.buildSite(url = it, tabId = tabId)
@@ -144,12 +171,23 @@ class TabDataRepository @Inject constructor(
144171
return data
145172
}
146173

174+
private suspend fun buildSiteData(url: String?, tabId: String): MutableLiveData<Site> {
175+
val data = MutableLiveData<Site>()
176+
url ?: return data
177+
val siteMonitor = siteFactory.buildSite(url = url, tabId = tabId)
178+
withContext(dispatchers.main()) {
179+
data.value = siteMonitor
180+
}
181+
return data
182+
}
183+
147184
private fun add(
148185
tabId: String,
149186
data: MutableLiveData<Site>,
150187
skipHome: Boolean,
151188
isDefaultTab: Boolean,
152189
sourceTabId: String? = null,
190+
updateIfBlankParent: Boolean = false,
153191
) {
154192
siteData[tabId] = data
155193
databaseExecutor().scheduleDirect {
@@ -178,6 +216,7 @@ class TabDataRepository @Inject constructor(
178216
position = position,
179217
sourceTabId = sourceTabId,
180218
),
219+
updateIfBlankParent = updateIfBlankParent,
181220
)
182221
}
183222
}

app/src/test/java/com/duckduckgo/tabs/db/TabsDaoTest.kt

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,4 +465,36 @@ class TabsDaoTest {
465465
assertNotNull(testee.tab(tab3.tabId))
466466
assertEquals(tab3, testee.selectedTab())
467467
}
468+
469+
@Test
470+
fun whenAddAndSelectWithBlankParentAndUpdateIfBlankParentTrueThenUpdateTabBeforeInserting() {
471+
val zero = TabEntity(
472+
"TAB_ID0",
473+
position = 0,
474+
sourceTabId = null,
475+
url = "http://example.com",
476+
)
477+
val first = TabEntity(
478+
"TAB_ID1",
479+
position = 1,
480+
sourceTabId = "TAB_ID0",
481+
url = null,
482+
)
483+
val second = TabEntity(
484+
"TAB_ID2",
485+
position = 2,
486+
sourceTabId = "TAB_ID1",
487+
url = "http://example.com",
488+
)
489+
490+
testee.insertTab(zero)
491+
testee.insertTab(first)
492+
testee.addAndSelectTab(second, updateIfBlankParent = true)
493+
494+
assertFalse(testee.tabs().contains(first))
495+
val storedTab = testee.tab("TAB_ID2")
496+
assertEquals("TAB_ID0", storedTab?.sourceTabId)
497+
assertEquals(1, storedTab?.position)
498+
assertEquals(storedTab, testee.selectedTab())
499+
}
468500
}

app/src/test/java/com/duckduckgo/tabs/model/TabDataRepositoryTest.kt

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -123,10 +123,28 @@ class TabDataRepositoryTest {
123123
testee.add("http://www.example.com")
124124

125125
val captor = argumentCaptor<TabEntity>()
126-
verify(mockDao).addAndSelectTab(captor.capture())
126+
verify(mockDao).addAndSelectTab(captor.capture(), any())
127127
assertTrue(captor.firstValue.viewed)
128128
}
129129

130+
@Test
131+
fun whenTabAddAndTabInsertionFixesOnThenAddAndSelectIsCalledWithUpdateIfBlankParent() = runTest {
132+
val testee = tabDataRepository()
133+
tabManagerFeatureFlags.tabInsertionFixes().setRawStoredState(State(enable = true))
134+
testee.add("http://www.example.com")
135+
136+
verify(mockDao).addAndSelectTab(any(), eq(true))
137+
}
138+
139+
@Test
140+
fun whenTabAddAndTabInsertionFixesOffThenAddAndSelectIsCalledWithUpdateIfBlankParentFalse() = runTest {
141+
val testee = tabDataRepository()
142+
tabManagerFeatureFlags.tabInsertionFixes().setRawStoredState(State(enable = false))
143+
testee.add("http://www.example.com")
144+
145+
verify(mockDao).addAndSelectTab(any(), eq(false))
146+
}
147+
130148
@Test
131149
fun whenTabUpdatedAfterOpenInBackgroundThenViewedIsTrue() = runTest {
132150
val testee = tabDataRepository()
@@ -170,7 +188,7 @@ class TabDataRepositoryTest {
170188
fun whenAddCalledThenTabAddedAndSelectedAndBlankSiteDataCreated() = runTest {
171189
val testee = tabDataRepository()
172190
val createdId = testee.add()
173-
verify(mockDao).addAndSelectTab(any())
191+
verify(mockDao).addAndSelectTab(any(), any())
174192
assertNotNull(testee.retrieveSiteData(createdId))
175193
}
176194

@@ -179,7 +197,7 @@ class TabDataRepositoryTest {
179197
val testee = tabDataRepository()
180198
val url = "http://example.com"
181199
val createdId = testee.add(url)
182-
verify(mockDao).addAndSelectTab(any())
200+
verify(mockDao).addAndSelectTab(any(), any())
183201
assertNotNull(testee.retrieveSiteData(createdId))
184202
assertEquals(url, testee.retrieveSiteData(createdId).value!!.url)
185203
}
@@ -229,7 +247,7 @@ class TabDataRepositoryTest {
229247
testee.add("http://www.example.com")
230248

231249
val captor = argumentCaptor<TabEntity>()
232-
verify(mockDao).addAndSelectTab(captor.capture())
250+
verify(mockDao).addAndSelectTab(captor.capture(), any())
233251
assertTrue(captor.firstValue.position == 0)
234252
}
235253

@@ -245,7 +263,7 @@ class TabDataRepositoryTest {
245263
testee.add("http://www.example.com")
246264

247265
val captor = argumentCaptor<TabEntity>()
248-
verify(mockDao).addAndSelectTab(captor.capture())
266+
verify(mockDao).addAndSelectTab(captor.capture(), any())
249267
assertTrue(captor.firstValue.position == 1)
250268
}
251269

0 commit comments

Comments
 (0)