Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
139 commits
Select commit Hold shift + click to select a range
2f84c06
Start Over
JPKribs Jan 17, 2026
3a7a2cb
Merge branch 'main' into tvOSPlayer
JPKribs Jan 17, 2026
eeb5254
WIP
JPKribs Jan 18, 2026
1d16117
WIP
JPKribs Jan 18, 2026
c4916a4
Burnt Bread
JPKribs Jan 18, 2026
8eda959
Merge branch 'main' into tvOSPlayer
JPKribs Jan 18, 2026
97f8b75
WIP
JPKribs Jan 18, 2026
8a6d829
Merge remote-tracking branch 'refs/remotes/origin/tvOSPlayer'
JPKribs Jan 18, 2026
e921166
Key Frames / Preview Images
JPKribs Jan 18, 2026
a73a38b
GestureView
JPKribs Jan 19, 2026
f480421
Merge branch 'main' into tvOSPlayer
JPKribs Jan 19, 2026
8f5c765
Media Info improvements & Supplement work - WIP
JPKribs Jan 22, 2026
e677d20
Functional but messy
JPKribs Jan 22, 2026
b2c3d33
Merge branch 'main' into tvOSPlayer
JPKribs Jan 23, 2026
90d5b78
Undo FocusGuide non-sense
JPKribs Jan 23, 2026
0fceacc
Merge branch 'main' into tvOSPlayer
JPKribs Jan 23, 2026
41c8d11
Fix close out issues
JPKribs Jan 23, 2026
9bd10c4
Merge remote-tracking branch 'refs/remotes/origin/tvOSPlayer'
JPKribs Jan 23, 2026
01eff17
Clenaup
JPKribs Jan 23, 2026
1c63637
Merge branch 'main' into tvOSPlayer
JPKribs Jan 25, 2026
3712298
Merge branch 'main' into tvOSPlayer
JPKribs Jan 29, 2026
6e6f60e
Merge branch 'main' into tvOSPlayer
JPKribs Feb 3, 2026
6a4bd33
WIP - Functional but messy
JPKribs Feb 13, 2026
6a4eab7
Merge branch 'main' into tvOSPlayer
JPKribs Feb 13, 2026
2dc799a
Cleanup
JPKribs Feb 14, 2026
ba8d7e8
WIP
JPKribs Feb 14, 2026
f1711ee
Cleanup: Move to bottom and consistent Episode vs Chapter Supplements
JPKribs Feb 14, 2026
4787d1e
Restore TODOs that were not resolved.
JPKribs Feb 14, 2026
fc696e3
Remove unneccessary changes
JPKribs Feb 14, 2026
ed8a336
Cleanup and work on Jump to Chapter/Episode
JPKribs Feb 15, 2026
13bc060
Merge-ish
JPKribs Feb 16, 2026
4e99a15
Add episode date to supplement. Add buffer so the gradient doesn't ge…
JPKribs Feb 16, 2026
3cd5b3c
Cleanup
JPKribs Feb 16, 2026
a0955ab
Performance on hardware and some bug fixes for scrubbing
JPKribs Feb 16, 2026
47d20c0
Outline Slider
JPKribs Feb 16, 2026
15fa50f
Cleanup focus on Supplement Title Button Style
JPKribs Feb 16, 2026
e77cfa1
FocusGuide
JPKribs Feb 18, 2026
e12ac1e
Cleanup
JPKribs Feb 18, 2026
624b4be
Fix iOS breaks. Some performance changes. Overall, just a giant mess …
JPKribs Feb 18, 2026
ee2dd7e
Start Over
JPKribs Jan 17, 2026
6baabf8
WIP
JPKribs Jan 18, 2026
7f3d8ba
WIP
JPKribs Jan 18, 2026
e5409ec
Burnt Bread
JPKribs Jan 18, 2026
3a3179e
WIP
JPKribs Jan 18, 2026
dab841f
Key Frames / Preview Images
JPKribs Jan 18, 2026
ef18bc6
GestureView
JPKribs Jan 19, 2026
04cadc7
Media Info improvements & Supplement work - WIP
JPKribs Jan 22, 2026
d6dedfd
Functional but messy
JPKribs Jan 22, 2026
3a38ee1
Undo FocusGuide non-sense
JPKribs Jan 23, 2026
491081e
Fix close out issues
JPKribs Jan 23, 2026
e182f58
Link: LLM/"AI" Development Policy into Contributor Documentation (#1905)
JPKribs Jan 23, 2026
5c01db7
Clenaup
JPKribs Jan 23, 2026
aa90b3a
Translated using Weblate (Italian)
giacomozop-hub Jan 24, 2026
e4555e2
Translated using Weblate (Turkish)
queeup Jan 24, 2026
789ae53
Translated using Weblate (Macedonian)
viktorilievski Jan 24, 2026
72d730d
Translated using Weblate (Turkish)
queeup Jan 25, 2026
537e2fb
Translated using Weblate (Turkish)
queeup Jan 25, 2026
1bc4f04
Translated using Weblate (Italian)
manmatteo Jan 27, 2026
b849f2d
Translated using Weblate (Dutch)
JoekeMeijer Jan 27, 2026
4b8018d
Translated using Weblate (French)
AntoineRoulin Jan 31, 2026
d1fe2f6
WIP - Functional but messy
JPKribs Feb 13, 2026
bec420e
Translated using Weblate (Chinese (Traditional Han script))
Feb 4, 2026
6826028
Added translation using Weblate (Estonian)
v3rm0n Feb 11, 2026
c91fc26
Translated using Weblate (Estonian)
v3rm0n Feb 11, 2026
74cf58f
Cleanup
JPKribs Feb 14, 2026
fabd9e1
WIP
JPKribs Feb 14, 2026
54a58b8
Cleanup: Move to bottom and consistent Episode vs Chapter Supplements
JPKribs Feb 14, 2026
fcaba81
Restore TODOs that were not resolved.
JPKribs Feb 14, 2026
93b748b
Remove unneccessary changes
JPKribs Feb 14, 2026
ad7f4ba
Cleanup and work on Jump to Chapter/Episode
JPKribs Feb 15, 2026
32e1da3
Merge-ish
JPKribs Feb 16, 2026
a1b7963
Add episode date to supplement. Add buffer so the gradient doesn't ge…
JPKribs Feb 16, 2026
86adab9
Cleanup
JPKribs Feb 16, 2026
ad6cd7c
Performance on hardware and some bug fixes for scrubbing
JPKribs Feb 16, 2026
797b1b5
Outline Slider
JPKribs Feb 16, 2026
805eab7
Cleanup focus on Supplement Title Button Style
JPKribs Feb 16, 2026
bcb7715
FocusGuide
JPKribs Feb 18, 2026
f54a8a7
Cleanup
JPKribs Feb 18, 2026
23d5215
Fix iOS breaks. Some performance changes. Overall, just a giant mess …
JPKribs Feb 18, 2026
cdeca2c
Merge remote-tracking branch 'refs/remotes/origin/tvOSPlayer'
JPKribs Feb 22, 2026
e0986b0
Merge remote-tracking branch 'upstream/main' into tvOSPlayer
JPKribs Feb 22, 2026
99c2825
Fallback line
JPKribs Feb 22, 2026
be90e7a
WIP
JPKribs Feb 22, 2026
b24ca78
Undo Swiftformat changes for otherwise untouched files
JPKribs Feb 22, 2026
72b927d
Cleanup
JPKribs Feb 22, 2026
68437d9
Cleanup
JPKribs Feb 22, 2026
1294fef
Remove changes with `.joined(separator: "\n\n")` on `PurgeUsedStrings…
JPKribs Feb 22, 2026
cdce4bc
Merge branch 'main' into tvOSPlayer
JPKribs Feb 22, 2026
07daeb2
Merge branch 'main' into tvOSPlayer
JPKribs Feb 23, 2026
089b898
Merge branch 'main' into tvOSPlayer
JPKribs Feb 24, 2026
8b744c7
Merge branch 'main' into tvOSPlayer
JPKribs Feb 25, 2026
eb6891d
Localize
JPKribs Feb 25, 2026
49d8a99
Merge branch 'main' into tvOSPlayer
JPKribs Feb 25, 2026
a7f09f0
Merge branch 'main' into tvOSPlayer
JPKribs Feb 28, 2026
7d16e42
Merge Fixes
JPKribs Feb 28, 2026
f27bc4d
Undo unnecessary linting changes
JPKribs Feb 28, 2026
238d148
Revert `@ViewBuilder` Removals
JPKribs Feb 28, 2026
c5e333f
Restore Linter Removed`@ViewBuilder`
JPKribs Feb 28, 2026
4394001
Linting Comment Cleanup
JPKribs Mar 1, 2026
bc6c26b
Fix animations for the supplements loading in at different times.
JPKribs Mar 1, 2026
b4ad2ba
Merge branch 'main' into tvOSPlayer
JPKribs Mar 1, 2026
9618fe2
FocusGuide cleanup. Animation Cleanup.
JPKribs Mar 1, 2026
d3840a0
Merge branch 'main' into tvOSPlayer
JPKribs Mar 2, 2026
e6ed61e
Merge branch 'main' into tvOSPlayer
JPKribs Mar 2, 2026
508b0f9
Merge branch 'main' into tvOSPlayer
JPKribs Mar 6, 2026
6535ccc
Merge branch 'main' into tvOSPlayer
JPKribs Mar 7, 2026
b28aae9
Merge Fixes
JPKribs Mar 7, 2026
23161aa
Merge branch 'main' into tvOSPlayer
JPKribs Mar 11, 2026
b77326d
Merge Fixes
JPKribs Mar 11, 2026
302fc3c
Smaller bar, Merge fixes, and FocusGuide cleanup
JPKribs Mar 11, 2026
b495be6
Merge branch 'main' into tvOSPlayer
JPKribs Mar 11, 2026
e7f7db2
Focus Fixes & notes for why.
JPKribs Mar 11, 2026
ec6fd89
Better Supplement management
JPKribs Mar 12, 2026
1c27278
Combine in more components from iOS. Remove `LoadingView` from tvOS s…
JPKribs Mar 12, 2026
71f4169
Linting
JPKribs Mar 12, 2026
792680e
Merge branch 'main' into tvOSPlayer
JPKribs Mar 12, 2026
7db9016
WIP
JPKribs Mar 13, 2026
c7bbf02
Remove focus loss
JPKribs Mar 13, 2026
3e10aef
Preview Image Spacing
JPKribs Mar 13, 2026
82860b3
Don't nuke supplements on focus loss
JPKribs Mar 13, 2026
0865b09
Revert supplement fix
JPKribs Mar 13, 2026
e671be4
Cleanup
JPKribs Mar 13, 2026
a8de6e8
Merge branch 'main' into tvOSPlayer
JPKribs Mar 13, 2026
d63151a
Merge branch 'main' into tvOSPlayer
JPKribs Mar 13, 2026
b257e4c
Focus cleanup
JPKribs Mar 14, 2026
26c5936
Focus Changes
JPKribs Mar 14, 2026
85b5c42
Focus Cleanup
JPKribs Mar 14, 2026
9607d4e
MORE focus fixes
JPKribs Mar 14, 2026
d1251f4
Focus Progress bar on overlay
JPKribs Mar 14, 2026
4ef17bc
Cleanup
JPKribs Mar 14, 2026
41615ee
Button cleanup
JPKribs Mar 14, 2026
3e841f3
Merge branch 'main' into tvOSPlayer
JPKribs Mar 14, 2026
8eb5212
Merge Fixes
JPKribs Mar 14, 2026
afd3b5b
Cleaning up compoennts: `SplitTimeStamp`
JPKribs Mar 14, 2026
e7457a7
Merge branch 'main' into tvOSPlayer
JPKribs Mar 14, 2026
eb07dbb
Merge fix
JPKribs Mar 14, 2026
f61b904
Break up the playback progress again since it was getting too tough t…
JPKribs Mar 14, 2026
2b2c09e
Merge branch 'main' into tvOSPlayer
JPKribs Mar 27, 2026
743de74
Localization
JPKribs Mar 27, 2026
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
3 changes: 2 additions & 1 deletion Scripts/Translations/AlphabetizeStrings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ let entries = strings.reduce(into: [String: String]()) {

// Sort the keys alphabetically for consistent ordering.
let sortedKeys = entries.keys.sorted { $0.localizedCaseInsensitiveCompare($1) == .orderedAscending }
let newContent = sortedKeys.map { "/// \(entries[$0]!)\n\"\($0)\" = \"\(entries[$0]!)\";" }.joined(separator: "\n\n")
let newContent = sortedKeys.map { "/// \(entries[$0]!)\n\"\($0)\" = \"\(entries[$0]!)\";" }
.joined(separator: "\n\n")

// Write the updated, sorted, and commented localizations back to the file.
do {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,14 @@ extension VideoPlayer.PlaybackControls.NavigationBar {
@EnvironmentObject
private var manager: MediaPlayerManager

#if os(tvOS)
@EnvironmentObject
private var focusGuide: FocusGuide

@FocusState
private var focusedButton: String?
#endif

private func filteredActionButtons(_ rawButtons: [VideoPlayerActionButton]) -> [VideoPlayerActionButton] {
var filteredButtons = rawButtons

Expand Down Expand Up @@ -71,7 +79,11 @@ extension VideoPlayer.PlaybackControls.NavigationBar {
case .autoPlay:
AutoPlay()
case .gestureLock:
#if os(iOS)
GestureLock()
#else
EmptyView()
#endif
case .playbackSpeed:
PlaybackRateMenu()
// case .playbackQuality:
Expand Down Expand Up @@ -110,10 +122,12 @@ extension VideoPlayer.PlaybackControls.NavigationBar {
@ViewBuilder
private var regularView: some View {
HStack(spacing: 0) {
ForEach(
barActionButtons,
content: view(for:)
)
ForEach(barActionButtons) { button in
view(for: button)
#if os(tvOS)
.focused($focusedButton, equals: button.rawValue)
#endif
}

if menuActionButtons.isNotEmpty {
Menu(
Expand All @@ -126,8 +140,23 @@ extension VideoPlayer.PlaybackControls.NavigationBar {
)
.environment(\.isInMenu, true)
}
#if os(tvOS)
.focused($focusedButton, equals: "menu")
#endif
}
}
#if os(tvOS)
.onChange(of: focusedButton) { _, newValue in
if focusGuide.focusedTag != newValue {
focusGuide.transition(to: newValue)
}
}
.onChange(of: focusGuide.focusedTag) { _, newTag in
if let newTag, newTag != focusedButton {
focusedButton = newTag
}
}
#endif
}

var body: some View {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@ extension VideoPlayer.PlaybackControls.NavigationBar.ActionButtons {
isAspectFilled.toggle()
}
.videoPlayerActionButtonTransition()
.id(isAspectFilled)
#if os(iOS)
.id(isAspectFilled)
#endif
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,10 @@ extension VideoPlayer.PlaybackControls.NavigationBar.ActionButtons {
}
}
.videoPlayerActionButtonTransition()
.id(isAutoPlayEnabled)
.disabled(manager.queue == nil)
#if os(iOS)
.id(isAutoPlayEnabled)
#endif
.disabled(manager.queue == nil)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ extension VideoPlayer.PlaybackControls {
@Router
private var router

private var fontSize: CGFloat = !UIDevice.isTV ? 24 : 36
private var fontWeight: Font.Weight = !UIDevice.isTV ? .semibold : .regular

private func onPressed(isPressed: Bool) {
if isPressed {
containerState.timer.stop()
Expand All @@ -31,37 +34,52 @@ extension VideoPlayer.PlaybackControls {
}
}

private var closeButton: some View {
Button {
if containerState.isPresentingSupplement {
containerState.select(supplement: nil)
} else {
manager.stop()
router.dismiss()
}
} label: {
AlternateLayoutView {
Image(systemName: "xmark")
} content: {
Label(
L10n.close,
systemImage: containerState.isPresentingSupplement ? "chevron.down" : "xmark"
)
}
.contentShape(Rectangle())
}
}

var body: some View {
HStack(alignment: .center) {
Button {
if containerState.isPresentingSupplement {
containerState.select(supplement: nil)
} else {
manager.stop()
router.dismiss()
}
} label: {
AlternateLayoutView {
Image(systemName: "xmark")
} content: {
Label(
L10n.close,
systemImage: containerState.isPresentingSupplement ? "chevron.down" : "xmark"
)
}
.contentShape(Rectangle())
HStack(alignment: UIDevice.isTV ? .bottom : .center) {

if !UIDevice.isTV {
closeButton
}

TitleView(item: manager.item)
.frame(maxWidth: .infinity, alignment: .leading)

ActionButtons()
}
.background {
EmptyHitTestView()
AlternateLayoutView(alignment: .trailing) {
Color.clear
.frame(maxWidth: .infinity, alignment: .trailing)
} content: {
ActionButtons()
.focusSection()
}
}
.font(.system(size: 24, weight: .semibold))
.font(.system(size: fontSize, weight: fontWeight))
.buttonStyle(OverlayButtonStyle(onPressed: onPressed))
#if os(iOS)
.background {
EmptyHitTestView()
}
#endif
}
}
}
Expand Down Expand Up @@ -97,7 +115,9 @@ extension VideoPlayer.PlaybackControls.NavigationBar {
var body: some View {
let titleSubtitle = self._titleSubtitle

#if os(iOS)
Text(titleSubtitle.title)
.font(.headline)
.fontWeight(.semibold)
.lineLimit(1)
.frame(minWidth: max(50, subtitleContentSize.width))
Expand All @@ -108,6 +128,20 @@ extension VideoPlayer.PlaybackControls.NavigationBar {
.offset(y: subtitleContentSize.height)
}
}
#else
VStack(alignment: .leading) {
Text(titleSubtitle.title)
.font(.headline)
.fontWeight(.semibold)
.lineLimit(1)

if let subtitle = titleSubtitle.subtitle {
_subtitle(subtitle)
.lineLimit(1)
}
}
.frame(minWidth: max(50, subtitleContentSize.width))
#endif
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ extension VideoPlayer.PlaybackControls.PlaybackProgress {
ForEach(unitPoints, id: \.self) { unitPoint in
if unitPoint > 0 {
Color.black
.frame(width: 1.5)
.frame(width: UIDevice.isTV ? 3 : 1.5)
.offset(x: proxy.size.width * unitPoint - 0.75)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ extension VideoPlayer.PlaybackControls {
if let runtime = manager.item.runtime, runtime > .zero {
GeometryReader { proxy in
Color.white
.frame(width: 1.5)
.frame(width: UIDevice.isTV ? 3 : 1.5)
.offset(x: proxy.size.width * (activeSeconds / runtime) - 0.75)
.frame(maxWidth: .infinity, alignment: .leading)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,29 +11,27 @@ import SwiftUI

extension VideoPlayer.PlaybackControls {

struct SplitTimeStamp: View {

@Default(.VideoPlayer.Overlay.trailingTimestampType)
private var trailingTimestampType
struct SplitTimeStamp: PlatformView {

@EnvironmentObject
private var containerState: VideoPlayerContainerState
@EnvironmentObject
private var scrubbedSecondsBox: PublishedBox<Duration>
@EnvironmentObject
private var manager: MediaPlayerManager

@State
private var activeSeconds: Duration = .zero

private var isScrubbing: Bool {
containerState.isScrubbing
}
@EnvironmentObject
private var scrubbedSecondsBox: PublishedBox<Duration>

private var scrubbedSeconds: Duration {
scrubbedSecondsBox.value
}

// MARK: - iOS

@Default(.VideoPlayer.Overlay.trailingTimestampType)
private var trailingTimestampType

@State
private var activeSeconds: Duration = .zero

@ViewBuilder
private var leadingTimestamp: some View {
HStack(spacing: 2) {
Expand All @@ -46,7 +44,7 @@ extension VideoPlayer.PlaybackControls {
Text(activeSeconds, format: .runtime)
}
.foregroundStyle(.secondary)
.isVisible(isScrubbing)
.isVisible(containerState.isScrubbing)
}
}

Expand All @@ -63,7 +61,7 @@ extension VideoPlayer.PlaybackControls {
Text("/")
}
.foregroundStyle(.secondary)
.isVisible(isScrubbing)
.isVisible(containerState.isScrubbing)

if let runtime = manager.item.runtime {
switch trailingTimestampType {
Expand All @@ -78,7 +76,8 @@ extension VideoPlayer.PlaybackControls {
}
}

var body: some View {
@ViewBuilder
var iOSView: some View {
HStack {
Button {
switch trailingTimestampType {
Expand Down Expand Up @@ -109,8 +108,48 @@ extension VideoPlayer.PlaybackControls {
.monospacedDigit()
.font(.caption2)
.lineLimit(1)
.foregroundStyle(isScrubbing ? .primary : .secondary, .secondary)
.foregroundStyle(containerState.isScrubbing ? .primary : .secondary, .secondary)
.assign(manager.secondsBox.$value, to: $activeSeconds)
}

// MARK: - tvOS

@State
private var contentSize: CGSize = .zero
@State
private var leadingTimestampSize: CGSize = .zero
@State
private var trailingTimestampSize: CGSize = .zero

private var scrubbedProgress: Double {
guard let runtime = manager.item.runtime, runtime > .zero else { return 0 }
return scrubbedSeconds / runtime
}

private var previewXOffset: CGFloat {
let p = contentSize.width * scrubbedProgress - (leadingTimestampSize.width / 2)
return clamp(p, min: 0, max: contentSize.width - (trailingTimestampSize.width + leadingTimestampSize.width))
}

@ViewBuilder
var tvOSView: some View {
ZStack {
if let runtime = manager.item.runtime {
Text(.zero - (runtime - scrubbedSeconds), format: .runtime)
} else {
Text(verbatim: .emptyRuntime)
}
}
.trackingSize($trailingTimestampSize)
.frame(maxWidth: .infinity, alignment: .trailing)
.overlay(alignment: .leading) {
Text(scrubbedSeconds, format: .runtime)
.trackingSize($leadingTimestampSize)
.offset(x: previewXOffset)
}
.font(.callout)
.monospacedDigit()
.trackingSize($contentSize)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,27 @@ extension VideoPlayer.PlaybackControls {

@Environment(\.isEnabled)
private var isEnabled
@Environment(\.isFocused)
private var isFocused

let onPressed: (Bool) -> Void

func makeBody(configuration: Configuration) -> some View {
configuration.label
.foregroundStyle(isEnabled ? AnyShapeStyle(HierarchicalShapeStyle.primary) : AnyShapeStyle(Color.gray))
.foregroundStyle(isEnabled ? isFocused ? AnyShapeStyle(Color.black) : AnyShapeStyle(HierarchicalShapeStyle.primary) :
AnyShapeStyle(Color.gray)
)
.labelStyle(.iconOnly)
.contentShape(Rectangle())
.scaleEffect(
configuration.isPressed ? 0.8 : 1
)
.animation(.bouncy(duration: 0.25, extraBounce: 0.25), value: configuration.isPressed)
.padding(4)
.padding(UIDevice.isTV ? 12 : 4)
.animation(nil, value: configuration.isPressed)
.background {
Circle()
.foregroundStyle(Color.white.opacity(configuration.isPressed ? 0.25 : 0))
.foregroundStyle(Color.white.opacity(configuration.isPressed ? 0.25 : isFocused ? 1 : 0))
.scaleEffect(configuration.isPressed ? 1 : 0.9)
}
.animation(.linear(duration: 0.1).delay(configuration.isPressed ? 0.2 : 0), value: configuration.isPressed)
Expand Down
4 changes: 4 additions & 0 deletions Shared/Objects/JumpProgressObserver.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ class JumpProgressObserver: ObservableObject {
}
}

func reset() {
jumps = 0
}

func jumpForward(interval: TimeInterval? = nil) {
if isForward {
jumps += 1
Expand Down
Loading
Loading