Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Check for compatible updates in plugin #832

Merged
merged 22 commits into from
Aug 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
ccbb647
Move the time utilities into a separate file
boatbomber Jan 5, 2024
342d43b
Add support for parsing string into version, comparing prereleases, a…
boatbomber Jan 5, 2024
197b4dd
Stylua formatting
boatbomber Jan 5, 2024
55802e7
Add setting to notify about updates
boatbomber Jan 5, 2024
0af90ca
Update changelog
boatbomber Jan 5, 2024
d10c398
Update Version.spec
boatbomber Jan 5, 2024
9c91909
Merge branch 'master' of https://github.com/rojo-rbx/rojo into plugin…
boatbomber Jan 6, 2024
7eae72f
Don't compare prereleases if only one has a number
boatbomber Jan 6, 2024
77d21b4
Merge branch 'master' of https://github.com/boatbomber/rojo into plug…
boatbomber Jan 17, 2024
c16d788
Stylua formatting
boatbomber Jan 17, 2024
fdb3dde
Merge branch 'master' into plugin-update-notif
boatbomber Jan 24, 2024
8871dca
Blank prerelease string is not a prerelease
boatbomber Jan 25, 2024
02cadc6
Only allow prerelease checks for locally managed plugin
boatbomber Jan 25, 2024
22d19de
How did the parentheses get screwed up in the merge?
boatbomber Jan 25, 2024
14df709
Remove debug print
boatbomber Jan 25, 2024
9df3b1b
Merge branch 'master' of https://github.com/rojo-rbx/rojo into plugin…
boatbomber Feb 1, 2024
a0607a5
Fix visible toggle
boatbomber Feb 1, 2024
5300c4a
Merge branch 'master' of https://github.com/boatbomber/rojo into plug…
boatbomber Feb 1, 2024
1732080
Merge branch 'master' of https://github.com/rojo-rbx/rojo into plugin…
boatbomber Feb 13, 2024
f379698
Merge branch 'master' of https://github.com/boatbomber/rojo into plug…
boatbomber Jul 26, 2024
99e7c83
Make update notifs opt-out
boatbomber Jul 26, 2024
f8dca5c
Fix merge mistake
boatbomber Jul 26, 2024
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
* Added popout diff visualizer for table properties like Attributes and Tags ([#834])
* Updated Theme to use Studio colors ([#838])
* Improved patch visualizer UX ([#883])
* Added update notifications for newer compatible versions in the Studio plugin. ([#832])
* Added experimental setting for Auto Connect in playtests ([#840])
* Improved settings UI ([#886])
* `Open Scripts Externally` option can now be changed while syncing ([#911])
Expand Down Expand Up @@ -75,6 +76,7 @@
**All** sync rules are reset between project files, so they must be specified in each one when nesting them. This is to ensure that nothing can break other projects by changing how files are synced!

[#813]: https://github.com/rojo-rbx/rojo/pull/813
[#832]: https://github.com/rojo-rbx/rojo/pull/832
[#834]: https://github.com/rojo-rbx/rojo/pull/834
[#838]: https://github.com/rojo-rbx/rojo/pull/838
[#840]: https://github.com/rojo-rbx/rojo/pull/840
Expand Down
27 changes: 3 additions & 24 deletions plugin/src/App/StatusPages/Connected.lua
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ local Packages = Rojo.Packages

local Roact = require(Packages.Roact)

local timeUtil = require(Plugin.timeUtil)
local Theme = require(Plugin.App.Theme)
local Assets = require(Plugin.Assets)
local PatchSet = require(Plugin.PatchSet)
Expand All @@ -20,28 +21,6 @@ local TableDiffVisualizer = require(Plugin.App.Components.TableDiffVisualizer)

local e = Roact.createElement

local AGE_UNITS = {
{ 31556909, "y" },
{ 2629743, "mon" },
{ 604800, "w" },
{ 86400, "d" },
{ 3600, "h" },
{ 60, "m" },
}
function timeSinceText(elapsed: number): string
local ageText = string.format("%ds", elapsed)

for _, UnitData in ipairs(AGE_UNITS) do
local UnitSeconds, UnitName = UnitData[1], UnitData[2]
if elapsed > UnitSeconds then
ageText = elapsed // UnitSeconds .. UnitName
break
end
end

return ageText
end

local ChangesViewer = Roact.Component:extend("ChangesViewer")

function ChangesViewer:init()
Expand Down Expand Up @@ -287,7 +266,7 @@ function ConnectedPage:getChangeInfoText()
if patchData == nil then
return ""
end
return timeSinceText(DateTime.now().UnixTimestamp - patchData.timestamp)
return timeUtil.elapsedToText(DateTime.now().UnixTimestamp - patchData.timestamp)
end

function ConnectedPage:startChangeInfoTextUpdater()
Expand All @@ -303,7 +282,7 @@ function ConnectedPage:startChangeInfoTextUpdater()
local updateInterval = 1

-- Update timestamp text as frequently as currently needed
for _, UnitData in ipairs(AGE_UNITS) do
for _, UnitData in ipairs(timeUtil.AGE_UNITS) do
local UnitSeconds = UnitData[1]
if elapsed > UnitSeconds then
updateInterval = UnitSeconds
Expand Down
19 changes: 19 additions & 0 deletions plugin/src/App/StatusPages/Settings/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,25 @@ function SettingsPage:render()
layoutOrder = layoutIncrement(),
}),

CheckForUpdates = e(Setting, {
id = "checkForUpdates",
name = "Check For Updates",
description = "Notify about newer compatible Rojo releases",
transparency = self.props.transparency,
layoutOrder = layoutIncrement(),
}),

CheckForPreleases = e(Setting, {
id = "checkForPrereleases",
name = "Include Prerelease Updates",
description = "Include prereleases when checking for updates",
transparency = self.props.transparency,
layoutOrder = layoutIncrement(),
visible = if string.find(debug.traceback(), "\n[^\n]-user_.-$") == nil
then false -- Must be a local install to allow prerelease checks
else Settings:getBinding("checkForUpdates"),
}),

AutoConnectPlaytestServer = e(Setting, {
id = "autoConnectPlaytestServer",
name = "Auto Connect Playtest Server",
Expand Down
101 changes: 75 additions & 26 deletions plugin/src/App/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ local PatchTree = require(Plugin.PatchTree)
local preloadAssets = require(Plugin.preloadAssets)
local soundPlayer = require(Plugin.soundPlayer)
local ignorePlaceIds = require(Plugin.ignorePlaceIds)
local timeUtil = require(Plugin.timeUtil)
local Theme = require(script.Theme)

local Page = require(script.Page)
Expand Down Expand Up @@ -118,6 +119,13 @@ function App:init()
end)
end)

self.disconnectUpdatesCheckChanged = Settings:onChanged("checkForUpdates", function()
self:checkForUpdates()
end)
self.disconnectPrereleasesCheckChanged = Settings:onChanged("checkForPrereleases", function()
self:checkForUpdates()
end)

self:setState({
appStatus = AppStatus.NotConnected,
guiEnabled = false,
Expand All @@ -131,32 +139,35 @@ function App:init()
toolbarIcon = Assets.Images.PluginButton,
})

if
RunService:IsEdit()
and self.serveSession == nil
and Settings:get("syncReminder")
and self:getLastSyncTimestamp()
and (self:isSyncLockAvailable())
then
self:addNotification("You've previously synced this place. Would you like to reconnect?", 300, {
Connect = {
text = "Connect",
style = "Solid",
layoutOrder = 1,
onClick = function(notification)
notification:dismiss()
self:startSession()
end,
},
Dismiss = {
text = "Dismiss",
style = "Bordered",
layoutOrder = 2,
onClick = function(notification)
notification:dismiss()
end,
},
})
if RunService:IsEdit() then
self:checkForUpdates()

if
Settings:get("syncReminder")
and self.serveSession == nil
and self:getLastSyncTimestamp()
and (self:isSyncLockAvailable())
then
self:addNotification("You've previously synced this place. Would you like to reconnect?", 300, {
Connect = {
text = "Connect",
style = "Solid",
layoutOrder = 1,
onClick = function(notification)
notification:dismiss()
self:startSession()
end,
},
Dismiss = {
text = "Dismiss",
style = "Bordered",
layoutOrder = 2,
onClick = function(notification)
notification:dismiss()
end,
},
})
end
end

if self:isAutoConnectPlaytestServerAvailable() then
Expand All @@ -179,6 +190,10 @@ end
function App:willUnmount()
self.waypointConnection:Disconnect()
self.confirmationBindable:Destroy()

self.disconnectUpdatesCheckChanged()
self.disconnectPrereleasesCheckChanged()

self.autoConnectPlaytestServerListener()
self:clearRunningConnectionInfo()
end
Expand Down Expand Up @@ -225,6 +240,40 @@ function App:closeNotification(id: number)
})
end

function App:checkForUpdates()
if not Settings:get("checkForUpdates") then
return
end

local isLocalInstall = string.find(debug.traceback(), "\n[^\n]-user_.-$") ~= nil
local latestCompatibleVersion = Version.retrieveLatestCompatible({
version = Config.version,
includePrereleases = isLocalInstall and Settings:get("checkForPrereleases"),
})
if not latestCompatibleVersion then
return
end

self:addNotification(
string.format(
"A newer compatible version of Rojo, %s, was published %s! Go to the Rojo releases page to learn more.",
Version.display(latestCompatibleVersion.version),
timeUtil.elapsedToText(DateTime.now().UnixTimestamp - latestCompatibleVersion.publishedUnixTimestamp)
),
500,
{
Dismiss = {
text = "Dismiss",
style = "Bordered",
layoutOrder = 2,
onClick = function(notification)
notification:dismiss()
end,
},
}
)
end

function App:getPriorEndpoint()
local priorEndpoints = Settings:get("priorEndpoints")
if not priorEndpoints then
Expand Down
2 changes: 2 additions & 0 deletions plugin/src/Settings.lua
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ local defaultSettings = {
twoWaySync = false,
showNotifications = true,
syncReminder = true,
checkForUpdates = true,
checkForPrereleases = false,
autoConnectPlaytestServer = false,
confirmationBehavior = "Initial",
largeChangesConfirmationThreshold = 5,
Expand Down
107 changes: 106 additions & 1 deletion plugin/src/Version.lua
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
local Packages = script.Parent.Parent.Packages
local Http = require(Packages.Http)
local Promise = require(Packages.Promise)

local function compare(a, b)
if a > b then
return 1
Expand Down Expand Up @@ -30,7 +34,48 @@ function Version.compare(a, b)
return minor
end

return revision
if revision ~= 0 then
return revision
end

local aPrerelease = if a[4] == "" then nil else a[4]
local bPrerelease = if b[4] == "" then nil else b[4]

-- If neither are prerelease, they are the same
if aPrerelease == nil and bPrerelease == nil then
return 0
end

-- If one is prerelease it is older
if aPrerelease ~= nil and bPrerelease == nil then
return -1
end
if aPrerelease == nil and bPrerelease ~= nil then
return 1
end

-- If they are both prereleases, compare those based on number
local aPrereleaseNumeric = string.match(aPrerelease, "(%d+).*$")
local bPrereleaseNumeric = string.match(bPrerelease, "(%d+).*$")

if aPrereleaseNumeric == nil or bPrereleaseNumeric == nil then
-- If one or both lack a number, comparing isn't meaningful
return 0
end
return compare(tonumber(aPrereleaseNumeric) or 0, tonumber(bPrereleaseNumeric) or 0)
end

function Version.parse(versionString: string)
local version = { string.match(versionString, "^v?(%d+)%.(%d+)%.(%d+)(.*)$") }
for i, v in version do
version[i] = tonumber(v) or v
end

if version[4] == "" then
version[4] = nil
end

return version
end

function Version.display(version)
Expand All @@ -43,4 +88,64 @@ function Version.display(version)
return output
end

function Version.retrieveLatestCompatible(options: {
version: { number },
includePrereleases: boolean?,
}): {
version: { number },
prerelease: boolean,
publishedUnixTimestamp: number,
}?
local success, releases = Http.get("https://api.github.com/repos/rojo-rbx/rojo/releases?per_page=10")
:andThen(function(response)
if response.code >= 400 then
local message = string.format("HTTP %s:\n%s", tostring(response.code), response.body)

return Promise.reject(message)
end

return response
end)
:andThen(Http.Response.json)
:await()

if success == false or type(releases) ~= "table" or next(releases) ~= 1 then
return nil
end

-- Iterate through releases, looking for the latest compatible version
local latestCompatible = nil
for _, release in releases do
-- Skip prereleases if they are not requested
if (not options.includePrereleases) and release.prerelease then
continue
end

local releaseVersion = Version.parse(release.tag_name)

-- Skip releases that are potentially incompatible
if releaseVersion[1] > options.version[1] then
continue
end

-- Skip releases that are older than the latest compatible version
if latestCompatible ~= nil and Version.compare(releaseVersion, latestCompatible.version) <= 0 then
continue
end

latestCompatible = {
version = releaseVersion,
prerelease = release.prerelease,
publishedUnixTimestamp = DateTime.fromIsoDate(release.published_at).UnixTimestamp,
}
end

-- Don't return anything if the latest found is not newer than the current version
if latestCompatible == nil or Version.compare(latestCompatible.version, options.version) <= 0 then
return nil
end

return latestCompatible
end

return Version
Loading
Loading