• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Lazerbeak12345 / sway-inv / 8888927211

30 Apr 2024 03:37AM UTC coverage: 94.043% (-0.2%) from 94.273%
8888927211

push

github

Lazerbeak12345
fix(400 pages): proper invalid page support

10 of 17 new or added lines in 1 file covered. (58.82%)

1 existing line in 1 file now uncovered.

221 of 235 relevant lines covered (94.04%)

4.58 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

93.53
/api.lua
1
local minetest, dump, flow, sway, flow_extras = minetest, dump, flow, sway, flow_extras
1✔
2
sway.pages = {}
1✔
3
sway.pages_ordered = {}
1✔
4
local contexts = {}
1✔
5
sway.enabled = true
1✔
6
local gui = flow.widgets
1✔
7

8
-- TODO: use fake tabheader
9

10
function sway.register_page(name, def)
1✔
11
        assert(type(name) == "string", "[sway] register_page: requires name to be string")
35✔
12
        assert(type(def) == "table", "[sway] register_page: requires definition table to be table")
33✔
13
        assert(type(def.get) == "function", "[sway] register_page: requires get inside the definition table to be function")
31✔
14
        assert(
58✔
15
                def.is_in_nav == nil or type(def.is_in_nav) == "function",
29✔
16
                "[sway] register_page: page '" .. name .. "' is_in_nav must be nil or a fn."
29✔
17
        )
18
        assert(not sway.pages[name], "[sway] register_page: page '" .. name .. "' must not already be registered")
28✔
19

20
        sway.pages[name] = def
27✔
21
        def.name = name
27✔
22
        table.insert(sway.pages_ordered, def)
27✔
23
end
24

25
function sway.override_page(name, def)
1✔
26
        assert(type(name) == "string", "[sway] override_page: requires name to be a string")
11✔
27
        assert(type(def) == "table", "[sway] override_page: requires definition table to be a table")
9✔
28
        local page = sway.pages[name]
7✔
29
        assert(type(page) == "table", "[sway] override_page: the page '" .. name .. "' could not be found to override")
7✔
30
        if type(def.name) ~= "nil" then
6✔
31
                assert(type(def.name) == "string", "[sway] override_page: When overriding the name, it must be a string.")
2✔
32
        end
33
        if type(def.get) ~= "nil" then
5✔
34
                assert(type(def.get) == "function", "[sway] override_page: When overriding get, it must be a function.")
2✔
35
        end
36
        assert(
8✔
37
                def.is_in_nav == nil or type(def.is_in_nav) == "function",
4✔
38
                "[sway] override_page: page '" .. name .. "' is_in_nav must be nil or a fn."
4✔
39
        )
40
        minetest.log("action", "[sway] override_page: '" .. name .. "' is becoming overriden")
3✔
41
        for key, value in pairs(def) do
7✔
42
                page[key] = value
4✔
43
        end
44
        if type(def.name) == "string" and name ~= def.name then
3✔
45
                minetest.log("action", "[sway] override_page: '" .. name .. "' is becoming renamed to '" .. page.name .. "'")
1✔
46
                sway.pages[page.name] = page
1✔
47
                sway.pages[name] = nil
1✔
48
        end
49
end
50

51
function sway.NavGui(fields)
1✔
52
        assert(type(fields) == "table", "[sway] NavGui: requires field table.")
10✔
53
        local nav_titles = fields.nav_titles
8✔
54
        assert(type(nav_titles) == "table", "[sway] NavGui: requires requires nav_titles to be a table.")
8✔
55
        local current_idx = fields.current_idx
6✔
56
        assert(type(current_idx) == "number", "[sway] NavGui: requires requires current_idx to be a number.")
6✔
57
        if #nav_titles > 1 then
4✔
58
                return gui.HBox{
4✔
59
                        gui.Spacer{ expand = false, w = .2 },
2✔
60
                        gui.Tabheader{
4✔
61
                                h = 1,
2✔
62
                                name = "sway_nav_tabs",
2✔
63
                                captions = nav_titles,
2✔
64
                                current_tab = current_idx,
2✔
65
                                transparent = true,
2✔
66
                                draw_border = false,
2✔
67
                                on_event = function(player, context)
68
                                        sway.set_page(player, context.nav[context.form.sway_nav_tabs])
1✔
69
                                end
70
                        }
71
                }
2✔
72
        else
73
                return gui.Nil{}
2✔
74
        end
75
end
76

77
function sway.InventoryTiles(fields)
1✔
78
        if fields == nil then
4✔
79
                fields = {}
1✔
80
        end
81
        local w = fields.w or 8
4✔
82
        local h = fields.h or 4
4✔
83
        return gui.VBox{
8✔
84
                align_v = "end",
4✔
85
                expand = true,
4✔
86
                flow_extras.List{
8✔
87
                        align_h = "center",
4✔
88
                        inventory_location = "current_player",
4✔
89
                        list_name = "main",
4✔
90
                        w = w,
4✔
91
                        h = 1,
4✔
92
                        bgimg = "sway_hb_bg.png"
4✔
93
                },
94
                h > 1 and flow_extras.List{
7✔
95
                        align_h = "center",
3✔
96
                        inventory_location = "current_player",
3✔
97
                        list_name = "main",
3✔
98
                        w = w,
3✔
99
                        h = h - 1,
3✔
100
                        starting_item_index = w
3✔
101
                } or gui.Nil{}
4✔
102
        }
4✔
103
end
104
local spacing = 0.25 -- TODO
1✔
105
function sway.insert_prepend(widget)
1✔
106
        widget.no_prepend = true -- Hide the default background.
1✔
107
        widget.bgcolor = "#0000"
1✔
108
        table.insert(widget, 1, gui.StyleType{ selectors = { "list" }, props = { spacing = spacing } })
1✔
109
end
110
function sway.Form(fields)
1✔
111
        assert(type(fields) == "table", "[sway] Form: requires field table.")
4✔
112
        local show_inv = fields.show_inv
2✔
113
        fields.show_inv = nil
2✔
114

115
        local context = sway.get_or_create_context()
2✔
116

117
        fields.padding = .4
2✔
118
        if show_inv then
2✔
119
                fields[#fields+1] = sway.InventoryTiles()
1✔
120
        end
121

122
        fields.name = "content"
2✔
123

124
        return gui.VBox{
4✔
125
                bgimg = "sway_bg_full.png",
2✔
126
                bgimg_middle = 12, -- Number of pixels from each edge.
2✔
127
                padding = 0,
2✔
128
                sway.NavGui{
4✔
129
                        nav_titles = context.nav_titles,
2✔
130
                        current_idx = context.nav_idx
2✔
131
                },
132
                gui.VBox(fields)
2✔
133
        }
2✔
134
end
135

136
function sway.get_homepage_name(_)
1✔
137
        return "sway:crafting"
7✔
138
end
139

140
sway.form = flow.make_gui(function (player, ctx)
2✔
141
        sway.set_context(player, ctx)
5✔
142
        local form = flow_extras.set_wrapped_context(ctx, function ()
8✔
143
                return sway.get_form(player, ctx)
3✔
144
        end)
145
        if not form.no_prepend then
2✔
146
                sway.insert_prepend(form)
1✔
147
        end
148
        return form
2✔
149
end)
150

151
function sway.get_form(player, context)
1✔
152
        player, context = sway.get_player_and_context(player, context)
6✔
153
        -- Generate navigation tabs
154
        local nav = {}
5✔
155
        local nav_ids = {}
5✔
156
        local requested_page = context.page
5✔
157
        local current_idx = -1 -- Defaults to this number to indicate an invalid page index.
5✔
158
        for _, pdef in ipairs(sway.pages_ordered) do
20✔
159
                if pdef.is_in_nav == nil or pdef:is_in_nav(player, context) then
15✔
160
                        nav[#nav + 1] = pdef.title
5✔
161
                        nav_ids[#nav_ids + 1] = pdef.name
5✔
162
                        if pdef.name == requested_page then
5✔
163
                                current_idx = #nav_ids
3✔
164
                        end
165
                end
166
        end
167
        context.nav = nav_ids
5✔
168
        context.nav_titles = nav
5✔
169
        context.nav_idx = current_idx
5✔
170

171
        -- Generate formspec
172
        local page = sway.pages[requested_page]
5✔
173
        if not page then
5✔
174
                page = sway.pages["404"]
1✔
175
                context.page = "404"
1✔
176
        elseif current_idx == -1 then
4✔
177
                page = sway.pages["403"]
1✔
178
                context.page = "403"
1✔
179
        end
180
        if page then
5✔
181
                return page:get(player, context)
5✔
182
        else
183
                local home_page = sway.get_homepage_name(player)
×
184

NEW
185
                if requested_page == home_page then
×
NEW
186
                        minetest.log(
×
187
                                "error",
188
                                "[sway] Couldn't find the requested page, '"
NEW
189
                                .. dump(requested_page)
×
NEW
190
                                .. "', which is also the home page."
×
191
                        )
UNCOV
192
                        return gui.Nil{}
×
193
                end
194

NEW
195
                minetest.log(
×
196
                        "warning",
197
                        "[sway] Couldn't find '"
NEW
198
                        .. dump(requested_page)
×
NEW
199
                        .. "' so switching to homepage"
×
200
                )
201
                sway.set_page(player, home_page)
×
202
                context = sway.get_or_create_context(player)
×
203
                assert(sway.pages[context.page], "[sway] Invalid homepage")
×
204

205
                return sway.get_form(player, context)
×
206
        end
207
end
208

209
local function ensure_valid_context(player, ctx)
210
        if not ctx.page then
6✔
211
                ctx.page = sway.get_homepage_name(player)
6✔
212
        end
213
        if not ctx.player_name then
6✔
214
                ctx.player_name = player:get_player_name()
6✔
215
        end
216
end
217

218
function sway.get_or_create_context(player)
1✔
219
        local context = flow_extras.get_context()
12✔
220
        if context then return context end
12✔
221
        assert(player, "[sway] get_or_create_context: Requires a playerref when run outside of a form.")
8✔
222
        local name = player:get_player_name()
6✔
223
        context = contexts[name]
6✔
224
        if not context then
6✔
225
                minetest.log("action", "[sway] get_or_create_context: creating new context for '" .. name .. "'")
4✔
226
                -- This must be the only place where a "fresh" context is generated.
227
                context = {}
4✔
228
                ensure_valid_context(player, context)
4✔
229
                contexts[name] = context
4✔
230
        end
231
        return context
6✔
232
end
233

234
function sway.get_player_and_context(player, context)
1✔
235
        if not context then
5✔
236
                context = sway.get_or_create_context(player)
3✔
237
        end
238
        if not player then
4✔
239
                player = minetest.get_player_by_name(context.player_name)
2✔
240
        end
241
        return player, context
4✔
242
end
243

244
function sway.set_context(player, context)
1✔
245
        assert(player and player.get_player_name, "[sway] set_context: Requires a playerref")
11✔
246
        local name = player:get_player_name()
9✔
247
        if not context then
9✔
248
                minetest.log("action", "[sway] set_context: deleting context for '" .. name .. "'")
7✔
249
        else
250
                ensure_valid_context(player, context)
2✔
251
        end
252
        contexts[name] = context
9✔
253
end
254

255
function sway.set_player_inventory_formspec(player, context)
1✔
256
        sway.form:set_as_inventory_for(sway.get_player_and_context(player, context))
2✔
257
end
258

259
function sway.set_page(player, new_page_name)
1✔
260
        local context = sway.get_or_create_context(player)
6✔
261
        local old_page = sway.pages[context.page]
6✔
262
        local type_new_page_name = type(new_page_name)
6✔
263
        assert(
12✔
264
                type_new_page_name == "string",
6✔
265
                "[sway] set_page: expected a string for the page name. Got a '" .. type_new_page_name .. "'"
6✔
266
        )
267
        local new_page = sway.pages[new_page_name]
5✔
268
        assert(new_page, "[sway] set_page: Page not found: '".. new_page_name .."'")
5✔
269
        -- We must be sure to assert all of the requirements before we take any action.
270
        if old_page and old_page.on_leave then
3✔
271
                old_page:on_leave(player, context)
1✔
272
        end
273
        context.page = new_page_name
3✔
274
        if new_page.on_enter then
3✔
275
                new_page:on_enter(player, context)
1✔
276
        end
277
        sway.set_player_inventory_formspec(player, context)
3✔
278
end
279

280
function sway.get_page(player)
1✔
281
        -- We don't need to do any checks here because
282
        -- 1. The context is guaranteed
283
        -- 2. Page is guaranteed to either be truthy or be the result of sway.get_homepage_name(player)
284
        return sway.get_or_create_context(player).page
1✔
285
end
286

287
minetest.register_on_joinplayer(function(player)
2✔
288
        if sway.enabled then
2✔
289
                sway.set_player_inventory_formspec(player)
1✔
290
        end
291
end)
292

293
minetest.register_on_leaveplayer(function(player)
2✔
294
        sway.set_context(player)
1✔
295
end)
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc